题目的意思是:给一串数字,然后在后面添加一个数字x问你新增了多少子串h[x],求这个数字x取从1到m时 3x*h[x]%mod的异或和。
思路:考虑新添加一个数字,增加的子串的数量为所有以该数字结尾的后缀,但是会出现前面已经出现过的子串,所以我们需要算出在没有添加之前出现过多少以该数字结尾的字符串,考虑将这些数反过来,然后用exkmp求出,next[i]: T[i~n-1]和T的最长公共前缀;对于1~m的每一个数字,我们只要求出其最大值即可,然后n-最大值就是这个数字的答案,将所有的答案异或就是最后的答案了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int mx[N];
typedef long long ll;
const ll mod=1e9+7;
int a[N];
int s[N];
void z_function(int s[],int n){
for(int i=1,l=0,r=0;i<n;++i){
if(i<=r&&a[i-l]<r-i+1){
a[i]=a[i-l];
}else{
a[i]=max(0,r-i+1);
while(i+a[i]<n&&s[a[i]]==s[i+a[i]]) ++a[i];
}
if(i+a[i]-1>r) l=i,r=i+a[i]-1;
}
}
int n,m;
int main(){
while(~scanf("%d%d",&n,&m)){
int maxx=0;
for(int i=0;i<n;++i){
scanf("%d",&s[i]);
maxx=max(maxx,s[i]);
a[i]=0;
}
for(int i=0;i<=m;++i){
mx[i]=0;
}
reverse(s,s+n);
z_function(s,n);
for(int i=0;i<n-1;++i){
mx[s[i]]=max(mx[s[i]],1+a[i+1]);
}
mx[s[n-1]]=max(mx[s[n-1]],1);
ll ans=0;
ll jc=1;
for(int i=1;i<=m;++i){
jc=jc*3%mod;
if(mx[i]==0){
ans^=((n+1)*jc%mod);
}
else{
ans^=(((n+1)-mx[i])*jc%mod);
}
}
printf("%lld\n",ans);
}
return 0;
}