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的做法,写着写着发现复杂度和正确性都假了……自闭。