20210527模拟赛总结

似乎今天的比较简单?(不是)

T1

原题,非常的简单,直接差分一下然后双指针搞一下就好了。

T2

看两个字符串,一定是串A的头和串B的尾相连,而A的前缀与B的后缀则是回文串的前后缀。

可以发现对于选出的A前缀a,B后缀b。首先将b翻转,发现两个串中较短者是另一个的前缀,这个性质显然。除此之外,去掉重复部分剩下的一定是一个回文串。

我们可以枚举较短串的长度,然后看能接上多少个回文串来统计答案。可以先使用manacher求出每个字符开始有多少个回文串的差分数组,然后再前缀和求出原数组,然后再来一遍前缀和便于求区间和即可。

并不是很轻松,但也不算是很难。

#include <cstdio>
#include <cstring>
#include <algorithm>

#define int long long

using namespace std;

int read()
{
	int a = 0,x = 1;char ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
	return a*x;
}
const int N=1e6+7,base=131,mod=19260817;
char s1[N],s2[N],tmp[N];
int n1,n2,a1[N],a2[N],r[N],m,f[N],hash1[N],hash2[N];

void init(char *s,int n,int *a)
{
	tmp[0] = '-';int len = 0;
	for(int i = 1;i <= n;i ++) {
		tmp[++len] = s[i];
		tmp[++len] = '-';
	}
	int p = 0,mx = 0;
	for(int i = 1;i <= len;i ++) {
		// printf("%d ",i);
		if(mx > i) r[i] = min(mx-i+1,r[2*p-i]);
		else r[i] = 0;
		while(i+r[i] <= len && i-r[i] >= 0) {
			if(tmp[i+r[i]] == tmp[i-r[i]]) r[i] ++;
			else break;
		}
		if(i+r[i]-1 > mx) mx = i+r[i]-1,p = i;
	}
	// printf("%s\n",tmp+1);
	// for(int i = 1;i <= len;i ++) printf("%d",r[i]);puts("");
	for(int i = 1;i < len;i ++) {
		if(i&1) {
			a[(i-r[i]+1)/2+1] ++;
			a[(i+1)/2+1] --;
			// printf("%d %d\n",(i-r[i]+1)/2+1,(i+1)/2);
		} else {
			a[(i-r[i])/2+2] ++;
			a[i/2+1] --;
			// printf("%d %d\n",(i-r[i])/2+2,(i)/2);
		}
	}
	for(int i = 1;i <= n+1;i ++) a[i] = a[i] + a[i-1];//printf("%d ",a[i]);puts("");
	for(int i = 1;i <= n+1;i ++) a[i] = a[i-1] + a[i];
}

bool check(int l1,int r1,int l2,int r2)
{
	return ((hash1[r1] - f[r1-l1+1]*hash1[l1-1]%mod)%mod+mod)%mod == ((hash2[r2] - f[r2-l2+1]*hash2[l2-1]%mod)%mod+mod)%mod;
}

signed main()
{
	freopen("palindrome.in","r",stdin);
	freopen("palindrome.out","w",stdout);
	scanf("%s",s1+1);
	scanf("%s %s",s1+1,s2+1);
	n1 = strlen(s1+1),n2 = strlen(s2+1);
	reverse(s2+1,s2+1+n2);f[0] = 1;
	for(int i = 1;i <= n1;i ++) hash1[i] = (hash1[i-1]*base + s1[i])%mod,f[i] = f[i-1]*base%mod;
	for(int i = 1;i <= n2;i ++) hash2[i] = (hash2[i-1]*base + s2[i])%mod;
	init(s1,n1,a1);init(s2,n2,a2);m = read();
	// for(int i = 1;i <= n1;i ++) printf("%d ",a1[i]-a1[i-1]);puts("");
	// for(int i = 1;i <= n2;i ++) printf("%d ",a2[i]-a2[i-1]);puts("");
	for(int i = 1;i <= m;i ++) {
		// printf("%d %d\n",p,q);
		// puts("!");
		int p = read(),q = read();
		// printf("%s\n%s\n",s1+p-1,s2+q-1);
		int l = 0,r = min(n1-p+1,n2-q+1),mid;
		while(l<r) {
			mid = l+r+1>>1;
			if(check(p,p+mid-1,q,q+mid-1)) l = mid;
			else r = mid-1;
		}
		int ans = 0;//printf("%d ",l);
		ans = (a1[p+l] - a1[p]) + (a2[q+l] - a2[q]);
		// printf("%lld - %lld + %lld - %lld \n",a1[p+l],a1[p],a2[q+l],a2[q]);
		printf("%lld\n",ans + l);
	}
	return 0;
}

另:模拟的时候第一想法居然是SA+st表求lcp,然后manacher求回文串再上线段树修改查询……直接爆炸。

T3

并不会,时间不太够了。一开始以为想出来了个nk的做法,写着写着发现复杂度和正确性都假了……自闭。

posted @ 2021-05-27 17:57  nao-nao  阅读(52)  评论(0编辑  收藏  举报