FZOUTSY 题解
题意简述
给定一个长度为 \(n\) 的,由特定 \(7\) 种字符构成的字符串,有 \(m\) 次询问,每次询问需要求出编号在 \([l,r]\) 内的后缀中,有多少对后缀的最长公共前缀长度大于等于 \(k\)。
题目分析
注意到在所有询问中,\(k\) 的值是一样的,所以我们可以考虑求出给定字符串中以第 \(i\) 个位置为开头,以第 \(i+k-1\) 个位置为结尾的区间的哈希值,随后,问题便转化为求在给定的区间内,有多少对数字相等。可以使用莫队解决。
代码
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
inline int read(){register int t1=0,t2=0;register char x=getchar();while(x<'0' ||x>'9'){if(x=='-') t2|=1;x=getchar();}while(x>='0' && x<='9'){t1=(t1<<1)+(t1<<3)+(x^48),x=getchar();}return t2?-t1:t1;}
inline void write(int x){register int sta[105],top=0;if(x<0) putchar('-'),x=-x;do{sta[top++]=x%10,x/=10;}while(x);while(top) putchar(sta[--top]+48);}
const int base=131;
int n,m,k,f[3000005],p[3000005],a[3000005],tot,t[3000005],ans[3000005],l=1,r,now,cnt[3000005],temp;
char c[3000005];
struct que{
int l,r,id;
}q[100005];
bool cmp(que x,que y){
if(x.l/temp!=y.l/temp) return x.l<y.l;
if((x.l/temp)&1) return x.r<y.r;
return x.r>y.r;
}
inline void add(int x){
now-=cnt[x]*cnt[x];
cnt[x]++;
now+=cnt[x]*cnt[x];
}
inline void del(int x){
now-=cnt[x]*cnt[x];
cnt[x]--;
now+=cnt[x]*cnt[x];
}
signed main(){
#ifdef cxy
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin>>n>>m>>k;
p[0]=1;
for(int i=1;i<=n;i++){
cin>>c[i];
f[i]=f[i-1]*base+(c[i]-'a'+1);
p[i]=p[i-1]*base;
}
for(int i=1;i+k-1<=n;i++){
a[i]=f[i+k-1]-f[i-1]*p[i+k-1-i+1];
t[i]=a[i];
tot++;
}
sort(t+1,t+1+tot);
temp=unique(t+1,t+1+tot)-t-1;
for(int i=1;i<=tot;i++) a[i]=lower_bound(t+1,t+1+temp,a[i])-t;
for(int i=1;i<=m;i++){
cin>>q[i].l>>q[i].r;
q[i].id=i;
q[i].r=min(q[i].r,n-k+1);
if(q[i].l>q[i].r){
q[i].l=1;
q[i].r=0;
}
}
temp=sqrt(m);
sort(q+1,q+1+m,cmp);
for(int i=1;i<=m;i++){
while(l>q[i].l) add(a[--l]);
while(r<q[i].r) add(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
ans[q[i].id]=now-(q[i].r-q[i].l+1)>>1;
}
for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
return 0;
}