2020牛客暑假多校第二场(解题报告)

题目链接:https://ac.nowcoder.com/acm/contest/5667 

A-All with Pairs 

题意: 给你n个字符串,函数f(s,t)表示字符串s的前缀和字符串t的后缀相同的最大长度,比如s=“abab”,t= “aba”,那么s的前缀分别是a,ab,aba,abab,t的后缀分别是a,ba,aba。其中相同的有a和aba这里取最大长度,所以f(s,t)=3。题目要求出\sum _{i=1}^{n}\sum _{j=1}^{n}f(s_i,s_j)^2(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:首先三个点就能够确定一个圆心(三角形的外心),然后这个圆又经过原点,那么就枚举两个点,跑O( n ^2 ),将圆心记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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43911947/article/details/107369844