BZOJ4516: [Sdoi2016]生成魔咒(后缀数组)

来源:互联网 发布:供应商主数据 编辑:程序博客网 时间:2024/05/17 07:18

传送门

给一个串,分别求[1,r] , r=1,2,3,4….,n的不同子串个数。

题解:后缀数组

先把串反转,其实就是求每一个后缀的不同子串个数。依次从后往前加入后缀,一个后缀能产生的不同子串个数为这个后缀的长度减去与它的排名前一名的后缀的长度,维护前缀即可。

每次插入一个后缀t,设它排名前一个后缀为p,后一个后缀为s。因为之前插入s或t后缀时必然会减去最长公共前缀,因此,答案先加上lcp(p,s)。再减去lcp(t,p),lcp(t,s)。求出height数组后rmp用st表预处理,查询时间复杂度O(1)。

对于前后缀的维护,用树状数组。总的时间复杂度O(nlogn)

#include<bits/stdc++.h>using namespace std;typedef pair<int,int> pii;const int Maxn=1e5+50,INF=0x3f3f3f3f;inline int read(){    char ch=getchar();int i=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}    return i*f;}int n,m,a[Maxn],tot,t1[Maxn],t2[Maxn],*rk=t1,*sa2=t2,sa[Maxn],h[Maxn],c[Maxn],mn[Maxn][30],Log[Maxn],Pow[30],pre[Maxn],suf[Maxn];pii b[Maxn];inline void Rsort(){    for(int i=1;i<=m;i++)c[i]=0;    for(int i=1;i<=n;i++)c[rk[i]]++;    for(int i=1;i<=m;i++)c[i]+=c[i-1];    for(int i=n;i>=1;i--)sa[c[rk[sa2[i]]]--]=sa2[i];}inline void getsa(){    for(int i=1;i<=n;i++)rk[i]=a[i],sa2[i]=i;    m=tot;Rsort();    for(int w=1,p=0;p<n;m=p,w<<=1)    {        p=0;        for(int i=n-w+1;i<=n;i++)sa2[++p]=i;        for(int i=1;i<=n;i++)if(sa[i]>w)sa2[++p]=sa[i]-w;        Rsort();swap(rk,sa2);rk[sa[1]]=p=1;        for(int i=2;i<=n;i++)rk[sa[i]]=(sa2[sa[i]]!=sa2[sa[i-1]]||sa2[sa[i]+w]!=sa2[sa[i-1]+w])?(++p):p;    }    for(int i=1,k=0,j;i<=n;h[rk[i++]]=k)    for(k?k--:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);}inline void getst(){    Pow[0]=1;Log[1]=0;    for(int i=1;i<=26;i++)Pow[i]=Pow[i-1]<<1;    for(int i=2;i<=n;i++)Log[i]=Log[i>>1]+1;    for(int i=1;i<=n;i++)mn[i][0]=h[i];    for(int i=1;i<=26&&Pow[i]<=n;i++)        for(int j=1;j+Pow[i]<=n+1;j++)        mn[j][i]=min(mn[j][i-1],mn[j+Pow[i-1]][i-1]);}inline int qmn(int x,int y){return min(mn[x][Log[y-x+1]],mn[y-Pow[Log[y-x+1]]+1][Log[y-x+1]]);}inline void ins(int x){    for(int t=x;t<=n;t+=(t&(-t)))pre[t]=max(pre[t],x);    for(int t=x;t;t-=(t&(-t)))suf[t]=min(suf[t],x);}inline int qpre(int x){    int res=0;    for(int t=x;t;t-=(t&(-t)))res=max(res,pre[t]);    return res;}inline int qsuf(int x){    int res=INF;    for(int t=x;t<=n;t+=(t&(-t)))res=min(res,suf[t]);    return res;}int buf[80];inline void W(long long x){    while(x)buf[++buf[0]]=x%10,x/=10;    while(buf[0])putchar('0'+buf[buf[0]--]);}int main(){    n=read();    for(int i=1;i<=n;i++)b[i].first=read(),b[i].second=n-i+1,suf[i]=INF;    sort(b+1,b+n+1);    for(int i=1;i<=n;i++)(i==1||b[i].first!=b[i-1].first)?(a[b[i].second]=++tot):(a[b[i].second]=tot);    getsa();    getst();    long long ans=0;    for(int i=n;i>=1;i--)    {        int p=qpre(rk[i]),s=qsuf(rk[i]),t=rk[i];        if(p&&s!=INF)ans+=qmn(p+1,s);        if(p)ans-=qmn(p+1,t);        if(s!=INF)ans-=qmn(t+1,s);        ans+=(n-i+1);ins(t);        printf("%lld\n",ans);    }}

(printf居然比输出优化快。。)