题目链接:https://ac.nowcoder.com/acm/contest/5667
A-All with Pairs
题意: 给你n个字符串,函数表示字符串s的前缀和字符串t的后缀相同的最大长度,比如s=“abab”,t= “aba”,那么s的前缀分别是a,ab,aba,abab,t的后缀分别是a,ba,aba。其中相同的有a和aba这里取最大长度,所以=3。题目要求出(mod 998244353)的结果。
输入:
3
ab
ba
aba
输出:
29
说明:
hint:字符串哈希+kmp
先计算出所有字符后缀的哈希值,然后用cnt统计出所有后缀的个数,再枚举所有字符串的前缀统计个数即可。但是会遇到一个问题,会多加。比如上次讨论的"abab"和"aba",有两对满足前后缀相同分别是"a"和"aba",但是题目要求的是最长的,这里就会多加了"a"的个数,而我们可以发现,其实"a"就是"aba"在kmp中的next数组的值,在统计前缀个数时,只需要减去前缀对应的next数组的值就好了,即前缀个数(ans += cnt[s] - cnt[next[s]])。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int N = 1e6+5;
const int base = 233;
const int mod = 998244353;
unordered_map<ULL,int>cnt;
int nxt[N],now[N];
string s[N];
void get_nxt(string s,int *nxt){//构建next数组
int i = 0 , j = -1 , len = s.size();
nxt[0] = -1 ;
while(i < len){
if(j == -1 || s[i] == s[j])nxt[++ i] = ++ j;
else j = nxt[j];
}
}
void Hash_pos(string s){//所有后缀的hash值
ULL ans = 0 , p = 1;
int len = s.size();
for(int i = len-1 ; i >= 0 ; i --){
ans = ans + p * (s[i] - 'a' + 1);
p = p * base ;
cnt[ans] ++ ;
}
}
void Hash_pre(string s){//所有前缀的hash值
int len = s.size();
ULL ans = 0;
for(int i = 0 ; i < len ; i ++){
ans = ans * base + (s[i] - 'a' + 1);
now[i] = cnt[ans];
}
}
LL get(int x){
return 1LL * x * x % mod;
}
int main(){
int n;cin>>n;
for(int i = 1 ; i <= n ; i ++){
cin >> s[i];
Hash_pos(s[i]);
}
ULL sum = 0;
for(int i = 1 ; i <= n ; i ++){
get_nxt(s[i] , nxt);
Hash_pre(s[i]);
int len = s[i].size();
//next[j]的前缀个数中要去掉j的前缀数量,因为题目要求取的是j这个前缀,而不是next[j]这个前缀
for(int j = 0 ; j < len ; j ++)now[nxt[j+1] - 1] -= now[j];
//计算结果
for(int j = 0 ; j < len ; j ++)sum = (sum + now[j] * get(j + 1)) % mod;
}
cout << sum << endl;
system("pause");
return 0;
}
B-Boundary
题意:给你n个坐标,让你随便定义一个圆心,首先这个圆必经过原点,然后问你最多能够经过多少个点。
输入:
4
1 1
0 2
2 0
2 2
输出:
3
hint:首先三个点就能够确定一个圆心(三角形的外心),然后这个圆又经过原点,那么就枚举两个点,跑,将圆心记pair,用map记录pair数量,用ans更新最大值即可,如果所有点都在一条直线上的话,那么最多只能经过2个点,除原点之外,最多只有1个点了。
#include<bits/stdc++.h>
#define PB push_back
#define PII pair<int,int>
#define PLL pair<long long,long long>
#define FI first
#define SE second
#define mem(a) memset(a,0,sizeof(a))
#define sc1(a) scanf("%d",&a)
#define sc2(a,b) scanf("%d%d",&a,&b)
#define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int INF = 0x3f3f3f3f;
const double pi=acos(-1),eps=1e-8;
const LL maxn = 1<<17;
const int mod = 1e9+7;
const int N = 4e5+5;
struct Point{
double x,y;
}a[2020];
map<pair<double,double>,int>mp;
int ans=0;
//过三点求圆心坐标
void solve(Point a,Point b,Point c)
{
double a1 = b.x - a.x, b1 = b.y - a.y, c1 = (a1*a1 + b1*b1)/2;
double a2 = c.x - a.x, b2 = c.y - a.y, c2 = (a2*a2 + b2*b2)/2;
double d = a1*b2 - a2*b1;
double x = a.x + (c1*b2 - c2*b1)/d, y = a.y + (a1*c2 -a2*c1)/d;
ans=max(ans,++mp[make_pair(x,y)]);
}
int main() {
int n;cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].x>>a[i].y;
}
for(int i=0;i<n;i++){
mp.clear();
for(int j=i+1;j<n;j++){
if(a[i].x*a[j].y==a[i].y*a[j].x)continue;
//三点共线的情况,只能经过2个点(原点和i,没有j,因此这里就先不考虑)
solve({0.0,0.0},a[i],a[j]);
//枚举的是j,最后ans还需加上i这个点
}
}
cout<<ans+1;
return 0;
}
F -Fake Maxpooling
题意:给你大小为n*m的矩阵,Ai,j大小是i,j的最小公倍数,其中(1<=i<=n,1<=j<=m),求所有k*k的子矩阵的最大值之和
输入:
3 4 2
输出:
38
hint:二维的滑动窗口,可以用两次单调队列求得k*k范围的最大值
#include<bits/stdc++.h>
#define PB push_back
#define PII pair<int,int>
#define PLL pair<long long,long long>
#define FI first
#define SE second
#define mem(a) memset(a,0,sizeof(a))
#define sc1(a) sacnf("%d",&a)
#define sc2(a,b) scanf("%d%d",&a,&b)
#define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int INF = 0x3f3f3f3f;
const double pi=acos(-1),eps=1e-8;
const LL maxn = 1<<17;
const int mod = 1e9+7;
const int N = 5005;
int dp[N][N],a[N][N];
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int lcm(int a,int b){
return a*b/gcd(a,b);
}
struct node{
int id,val;
}tmp;
int main() {
int n,m,k;
sc3(n,m,k);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j]=lcm(i,j);
}
}
//对每行做一次滑动窗口
for(int i=1;i<=n;i++){
deque<node>q;
//由于是求区间最大值,那么这里应该是递减队列
for(int j=1;j<=m;j++){
if(q.size()&&j-q.front().id>=k)q.pop_front();
while(q.size()&&a[i][j]>q.back().val)q.pop_back();
tmp=(node){j,a[i][j]};
q.push_back(tmp);
if(j>=k)dp[i][j]=q.front().val;
}
}
LL ans=0;
//对每列做一次滑动窗口,从k行开始
for(int j=k;j<=m;j++){
deque<node>q;
for(int i=1;i<=n;i++){
if(q.size()&&i-q.front().id>=k)q.pop_front();
while(q.size()&&dp[i][j]>q.back().val)q.pop_back();
tmp=(node){i,dp[i][j]};
q.push_back(tmp);
if(i>=k)ans+=q.front().val;
}
}
cout<<ans;
return 0;
}