初始有一个空串。每次在末尾添加一个字符(字符集是[1,1000000000]的数字),并询问当前有多少本质不同的子串。
虽然字符集很大,但是可以map耶有木有。
n log n就可以过啦,还可以在线的很兹瓷嘛!
由于我是蒟蒻,所以这题我最终打的是第二眼看出来的算法——后缀数组
首先它没有强制在线!
于是可以先把最终串搞出来。
然后把最终串倒过来。那么问题就被转化为:
n个询问第i个询问是S的区间[i,n]有多少本质不同的子串。
那么我们先把SA搞出来。
于是每次就相当于插入一个后缀,然后要想办法更新本质不同的子串个数。
具体的,我们需要知道插入新后缀i后这个新后缀的前一个和后一个后缀是谁,记为j和k。
那么答案先减去j和k对答案的贡献,然后加上j和i以及i和k对答案的贡献。
注意一个细节(我之前挂了)——i可能没有前一个或后一个后缀(即j和k可能为0),不过处理很简单,计算x和y对答案造成的影响,如果y是0影响就为0,y不是0不管x是不是0都可以统一处理。
计算x和y对答案的贡献贡献:n-y+1-lcp(x,y)
即y的每一个前缀子串,减去和x的前缀子串重复的部分。lcp表示最长公共前缀。
求lcp是sa的基本功能,x和y的lcp其实就是区间[x+1,y]的height的最小值。最小值用rmq维护即可。
现在来说说怎么找到j和k,反正本蒟蒻是打了线段树……
还要一点,注意开long long呀!
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
int s[maxn];
int sa[maxn],rank[maxn*2],height[maxn],sta[maxn],b[maxn],c[maxn],d[maxn];
int two[25],tree[maxn*4],rmq[maxn][20];
int i,j,k,l,t,n,m;
ll ans;
void getsa(){
fo(i,1,n) rank[i]=s[i];
j=1;
while (j<=n){
fo(i,0,n) b[i]=0;
fo(i,1,n) b[rank[i+j]]++;
fo(i,1,n) b[i]+=b[i-1];
fd(i,n,1) c[b[rank[i+j]]--]=i;
fo(i,0,n) b[i]=0;
fo(i,1,n) b[rank[i]]++;
fo(i,1,n) b[i]+=b[i-1];
fd(i,n,1) d[b[rank[c[i]]]--]=c[i];
t=0;
fo(i,1,n){
if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) t++;
c[d[i]]=t;
}
fo(i,1,n) rank[i]=c[i];
if (t==n) break;
j*=2;
}
fo(i,1,n) sa[rank[i]]=i;
}
void getheight(){
k=0;
fo(i,1,n){
if (k) k--;
j=sa[rank[i]-1];
while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
height[rank[i]]=k;
}
}
void insert(int p,int l,int r,int a){
tree[p]++;
if (l==r) return;
int mid=(l+r)/2;
if (a<=mid) insert(p*2,l,mid,a);else insert(p*2+1,mid+1,r,a);
}
int getmax(int p,int l,int r,int a,int b){
if (a>b) return 0;
if (l==r) return tree[p]?l:0;
int mid=(l+r)/2;
if (b<=mid) return getmax(p*2,l,mid,a,b);
else if (a>mid) return getmax(p*2+1,mid+1,r,a,b);
else{
int t=getmax(p*2+1,mid+1,r,mid+1,b);
if (t) return t;
return getmax(p*2,l,mid,a,mid);
}
}
int getmin(int p,int l,int r,int a,int b){
if (a>b) return 0;
if (l==r) return tree[p]?l:0;
int mid=(l+r)/2;
if (b<=mid) return getmin(p*2,l,mid,a,b);
else if (a>mid) return getmin(p*2+1,mid+1,r,a,b);
else{
int t=getmin(p*2,l,mid,a,mid);
if (t) return t;
return getmin(p*2+1,mid+1,r,mid+1,b);
}
}
int lcp(int l,int r){
l++;
if (l>r) return 0;
int j=floor(log(r-l+1)/log(2));
return min(rmq[l][j],rmq[r-two[j]+1][j]);
}
int main(){
two[0]=1;
fo(i,1,25) two[i]=two[i-1]*2;
scanf("%d",&n);
fo(i,1,n) scanf("%d",&s[i]),b[i]=s[i];
sort(b+1,b+n+1);
l=unique(b+1,b+n+1)-b-1;
fo(i,1,n) s[i]=lower_bound(b+1,b+l+1,s[i])-b;
fo(i,1,n/2) swap(s[i],s[n-i+1]);
getsa();
getheight();
fo(i,1,n) rmq[i][0]=height[i];
fo(j,1,floor(log(n)/log(2)))
fo(i,1,n-two[j-1])
rmq[i][j]=min(rmq[i][j-1],rmq[i+two[j-1]][j-1]);
fd(i,n,1){
j=getmax(1,1,n,1,rank[i]-1);
k=getmin(1,1,n,rank[i]+1,n);
if (k) ans-=(ll)n-sa[k]+1-lcp(j,k);
ans+=(ll)n-i+1-lcp(j,rank[i]);
if (k) ans+=(ll)n-sa[k]+1-lcp(rank[i],k);
printf("%lld\n",ans);
insert(1,1,n,rank[i]);
}
}