[HDU5659]knight

来源:互联网 发布:linux测试网速的命令 编辑:程序博客网 时间:2024/06/07 16:20

题目大意

求一个字符串,从每一个位置分成两部分(当做两个字符串),求本质不同子串数量。

SAM

我们先有一个简单的思路。f[i]表示从i分成两部分的答案。
正难则反,求出原串本质不同子串数量,对于每个位置减去跨线且没在两边出现过的即可。
对于一个子串,如果其出现在原串中最大的右端点为mx,最小的右端点为mi,其长度为len。
如果mx-len>=mi,则因为其所有出现区间交为空,无论从哪个位置分开这个子串都会出现在其中一边。
如果mxlen<mi,那么f的[mx-len,mi-1]都可以加一,因为显然在区间交里都可以分离成功。
然而子串个数很多,怎么办呢?
考虑SAM,那么公用right集合的点被缩为一个点,枚举自动机上每个点,然后得到mx和mi(这里说一下mx和mi的求法,因为后缀自动机上一点的right集合是其parent树中子树的right集合的并,因此其mx等于其子树中mx的最大值,mi等于其子树中mi的最小值),同时要得到len的范围[l,r](后缀自动机上一个点的maxs就是step,mins就是其parent树父亲的maxs+1)
那么f的[mx-r+1,mx-l+1]是第一个+1,第二个+2,第三个+3……一直这样下去,然后f的[mx-l+2,mi-1]+r-l+1。
我们考虑线性做,可以设gi=fi-fi-1,那么区间加等差变成了区间+1,后面的平板只需要单点修改。区间加1可以通过开头挂+1结尾挂-1最后线性扫实现。

#include<cstdio>#include<algorithm>#include<cstring>#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=500000+10,mo=1000000007;int g[maxn*2][10],pre[maxn*2],step[maxn*2],mi[maxn*2],ma[maxn*2],mins[maxn*2],maxs[maxn*2],cnt[maxn*2],ad[maxn],f[maxn];int a[maxn*2];int i,j,k,l,r,t,n,m,tot,last,sum,ans;char ch;char get(){    char ch=getchar();    while (ch<'0'||ch>'9') ch=getchar();    return ch;}void add(int x){    int np=++tot,p=last;    mi[tot]=100000000;    step[np]=step[p]+1;    while (p>=0&&g[p][x]==0){        g[p][x]=np;        p=pre[p];    }    if (p==-1) pre[np]=0;    else{        int q=g[p][x];        if (step[q]==step[p]+1) pre[np]=q;        else{            int nq=++tot,i;            mi[tot]=100000000;            fo(i,0,9) g[nq][i]=g[q][i];            pre[nq]=pre[q];            pre[q]=nq;            step[nq]=step[p]+1;            pre[np]=nq;            while (p>=0&&g[p][x]==q){                g[p][x]=nq;                p=pre[p];            }        }    }    last=np;}int quicksortmi(int x,int y){    if (!y) return 1;    int t=quicksortmi(x,y/2);    t=(ll)t*t%mo;    if (y%2) t=(ll)t*x%mo;    return t;}int main(){    freopen("knight6.in","r",stdin);    scanf("%d",&n);    pre[0]=-1;     fo(i,1,n){        ch=get();        add(ch-'0');        ma[last]=mi[last]=i;    }    fo(i,1,tot) sum=(sum+step[i]-step[pre[i]])%mo;    /*fd(i,tot,1){        mi[pre[i]]=min(mi[pre[i]],mi[i]);        ma[pre[i]]=max(ma[pre[i]],ma[i]);        maxs[i]=step[i];        mins[i]=step[pre[i]]+1;    }*/    fo(i,0,tot) cnt[step[i]]++;    fo(i,0,step[last]) cnt[i]+=cnt[i-1];    fo(i,0,tot) a[cnt[step[i]]--]=i;    fd(i,tot+1,1){        mi[pre[a[i]]]=min(mi[pre[a[i]]],mi[a[i]]);        ma[pre[a[i]]]=max(ma[pre[a[i]]],ma[a[i]]);        maxs[a[i]]=step[a[i]];        mins[a[i]]=step[pre[a[i]]]+1;    }    fo(i,1,tot){        l=mins[i];r=maxs[i];        l=max(l,ma[i]-mi[i]+1);        if (l>r) continue;        ad[ma[i]-r+1]++;        ad[ma[i]-l+2]--;        f[mi[i]]-=(r-l+1);    }    k=0;    fo(i,1,n){        k=(k+ad[i])%mo;        f[i]=(f[i]+k);    }    fo(i,1,n) f[i]=(f[i]+f[i-1])%mo;    fo(i,1,n) f[i]=sum-f[i];    fo(i,1,n-1) ans=(ans+(ll)f[i]*quicksortmi(100013,n-i-1)%mo)%mo;    (ans+=mo)%=mo;    printf("%d\n",ans);}
0 0