1. 程式人生 > >【[SDOI2016]生成魔咒】

【[SDOI2016]生成魔咒】

ans char oid -h 排名 new ring 動態 答案

這是一道\(SA\)的練手好題

建議做之前先去做一下2408

之後你就肯定會做這道題了

首先上面那道題的答案就是

\[\sum_{i=1}^nn+1-sa[i]-het[i]\]

就是對於每一個後綴求出其能產生的子串,之後減掉和之前本質相同的子串

對於這個題,我們需要求出所有前綴的本質不同的子串個數

先無腦敲上\(sa\)\(het\)的板子,之後我們只需要往裏面動態添加後綴就好了

但是如果正著處理的話會有一個非常顯然的問題,也就是我們加進去一個後綴,但是這個後綴和之前的一些後綴形成的\(lcp\)長度超過當前的長度,會導致我們很難計算

所以我們需要把字符串倒過來,之後每次往裏面添加一個後綴就只相當於往裏面添加了一個字符

反置字符串顯然不會令子串變得不相等,於是我們可以完美解決這個問題

之後我們維護上面的那個柿子就好了,由於我們插入的\(sa\)值並不連續,所以我們不能直接用\(het\),而是\(het\)的最小值

於是我們用一個\(st\)表來查詢\(het\)的最小值,之後每插入一個點相當於要斷裂一個原來存在的排名連續的後綴,所以還需要一個\(set\)來找前驅和後繼

代碼

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<set>
#define re register
#define LL long long
#define maxn 100005
#define set_it std::set<int>::iterator
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    re char c=getchar();int x=0;
    while(c<‘0‘||c>‘9‘) c=getchar();
    while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int a[maxn],rk[maxn],tp[maxn],tax[maxn],sa[maxn],het[maxn],b[maxn],to[maxn];
int St[maxn][18],log_2[maxn];
int n,m,sz;LL ans=1;
std::set<int> s;
inline void qsort()
{
    for(re int i=0;i<=m;i++) tax[i]=0;
    for(re int i=1;i<=n;i++) tax[rk[i]]++;
    for(re int i=1;i<=m;i++) tax[i]+=tax[i-1];
    for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i];
}
inline int Pre(int x)
{
    s.insert(x); set_it i=s.find(x);
    if(i==s.begin()) return -1; --i; return *i;
}
inline int Nxt(int x) {set_it i=s.find(x);++i;if(i==s.end()) return -1;return *i;}
inline int find(int x)
{
    int l=1,r=sz;while(l<=r)
    {
        int mid=l+r>>1;if(b[mid]==x) return mid;
        if(b[mid]<x) l=mid+1;else r=mid-1;
    }return 0;
}
inline int ask(int l,int r) {int k=log_2[r-l+1];return min(St[l][k],St[r-(1<<k)+1][k]);}
int main()
{
    n=read();for(re int i=n;i;--i) a[i]=read(),b[i]=a[i];
    std::sort(b+1,b+n+1);m=sz=std::unique(b+1,b+n+1)-b-1;
    for(re int i=1;i<=n;i++) a[i]=find(a[i]);
    for(re int i=1;i<=n;i++) rk[i]=a[i],tp[i]=i;
    qsort();
    for(re int w=1,p=0;p<n;w<<=1,m=p)
    {
        p=0;
        for(re int i=1;i<=w;i++) tp[++p]=n-w+i;
        for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
        qsort();for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]);
        rk[sa[1]]=p=1;
        for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
    }
    int k=0;
    for(re int i=1;i<=n;i++)
    {
        if(k) --k;
        int j=sa[rk[i]-1];
        while(a[i+k]==a[j+k]) ++k;
        het[rk[i]]=k;
    }
    for(re int i=2;i<=n;i++) log_2[i]=1+log_2[i>>1];
    for(re int i=1;i<=n;i++) St[i][0]=het[i];
    for(re int j=1;j<=17;j++)
        for(re int i=1;i+(1<<j)-1<=n;i++)
            St[i][j]=min(St[i][j-1],St[i+(1<<(j-1))][j-1]);puts("1");s.insert(rk[n]);
    for(re int i=n-1;i;--i)
    {
        ans+=n-i+1;
        int x=Pre(rk[i]);
        if(x!=-1) {int t=ask(x+1,rk[i]);ans+=to[x],ans-=t;to[x]=t;}
        x=Nxt(rk[i]);
        if(x!=-1) to[rk[i]]=ask(rk[i]+1,x),ans-=to[rk[i]];
        printf("%lld\n",ans);
    }
    return 0;
}

【[SDOI2016]生成魔咒】