【BZOJ4516】【Sdoi2016】生成魔咒 后缀数组 线段树

ZJOI2015陈老师幻想乡的简化(阉割)版,所以可以不用Trie上后缀自动机那么高大上的东西。。。

首先不难想象出O(n^4)以及O(n^2)的做法,一种是每穷举一个字串再暴力检查是否已经出现,一种是在第一种的基础上Hash优化。

这两种都没有什么卵用我们就不提了。

首先我们将数字串反向读入,那么每一个加入的前缀对应反串的一个后缀,我们每加入一个字母,相当于有 该字母开始的后缀和目前已经加入的后缀的最大公共部分重复 ,剩下的就是全新的,用线段树维护区间最大最小即可。

</pre><div class="line number1 index0 alt2" style="font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; line-height: 17.6px; font-size: 16px; border-radius: 0px !important; border: 0px !important; bottom: auto !important; float: none !important; height: auto !important; left: auto !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px 1em !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; width: auto !important; box-sizing: content-box !important; min-height: auto !important; white-space: pre !important; background-image: none !important; background-attachment: initial !important; background-size: initial !important; background-origin: initial !important; background-clip: initial !important; background-position: initial !important; background-repeat: initial !important;"><pre name="code" class="cpp">/**************************************************************
    Problem: 4516
    User: RicardoWang
    Language: C++
    Result: Accepted
    Time:1700 ms
    Memory:11824 kb
****************************************************************/
 
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define maxn 100005
void _read(int &x)
{
    bool flag=false;char ch=getchar(); x=0;
    while(ch<'0'||ch>'9'){if(ch=='-')flag=true; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return    ;
}
int a[maxn],b[maxn],n,tot,m;
void Init()
{
    _read(n);for(int i=n;i>=1;i--){_read(a[i]);b[i]=a[i];}
    sort(b+1,b+1+n);tot=unique(b+1,b+1+n)-b-1;m=tot;
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
    return ;
}
int sa[maxn],height[maxn],wa[maxn*2],wb[maxn*2],cc[maxn*2],rank[maxn];
void makesa()
{
    int *x=wa,*y=wb,ct,*t;
    for(int i=1;i<=m;i++)cc[i]=0;
    for(int i=1;i<=n;i++)cc[x[i]=a[i]]++;
    for(int i=1;i<=m;i++)cc[i]+=cc[i-1];
    for(int i=n;i>=1;i--)sa[cc[x[i]]--]=i;
    for(int k=1;k<=n;k=k<<1)
    {
        ct=0;for(int i=n-k+1;i<=n;i++)y[++ct]=i;
        for(int i=1;i<=n;i++)if(sa[i]>k)y[++ct]=sa[i]-k;
        for(int i=1;i<=m;i++)cc[i]=0;
        for(int i=1;i<=n;i++)cc[x[y[i]]]++;
        for(int i=1;i<=m;i++)cc[i]+=cc[i-1];
        for(int i=n;i>=1;i--)sa[cc[x[y[i]]]--]=y[i];
        t=x;x=y;y=t; x[sa[1]]=1; ct=1;
        for(int i=2;i<=n;i++)x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])?ct:++ct;
        m=ct; if(m>=n)break;
    }
    return ;
}
void calcheight()
{
    int j;
    for(int i=1;i<=n;i++)rank[sa[i]]=i;
    int k=0;
    for(int i=1;i<=n;i++)
    {
        if(k)k--;
        j=sa[rank[i]-1];
        while(a[j+k]==a[i+k])k++;
        height[rank[i]]=k;
    }
    return ;
}
int np,rt1,rt2,minv[4*maxn],maxv[4*maxn],chi[4*maxn][2];
void build(int &now,int L,int R ,bool op) //op=false 维护已加入的rank,op==false 维护height 
{
    now=++np;
    if(L==R)
    {
        if(op)minv[now]=maxv[now]=height[L];
        else minv[now]=n+1;maxv[now]=-1;
        return ;
    }
    int m=(L+R)>>1; build(chi[now][0],L,m,op);build(chi[now][1],m+1,R,op);
    minv[now]=min(minv[chi[now][0]],minv[chi[now][1]]);
    maxv[now]=max(maxv[chi[now][0]],maxv[chi[now][1]]);
    return ;
}
void update(int now,int L,int R,int x)
{
    if(L==R){minv[now]=maxv[now]=x;return ;}
    int m=(L+R)>>1;
    if(x<=m)update (chi[now][0],L,m,x); else update(chi[now][1],m+1,R,x);
    minv[now]=min(minv[chi[now][0]],minv[chi[now][1]]);
    maxv[now]=max(maxv[chi[now][0]],maxv[chi[now][1]]);
    return ;
}
int query(int now,int L,int R,int x,int y,bool op)//op==true -> min  op==false -> max
{
    if(x>y)
    {
        return op? n+1:-1;
    }
    if(x<=L && R<=y) return op?minv[now]:maxv[now];
    int m=(L+R)>>1;
    int t1,t2; t1=t2=op? n+1:-1;
    if(x<=m)
    {
        t1=query(chi[now][0],L,m,x,y,op);
    }
    if(y>m)
    {
        t2=query(chi[now][1],m+1,R,x,y,op);
    }
    return op? min(t1,t2):max(t1,t2);
}
long long ans;
int l,r,t;
char s[35];int cct;
void out(long long x)
{
    cct=0; while(x){s[++cct]=x%10+'0';x=x/10;}
    while(cct){putchar(s[cct]);cct--;}
    putchar('\n');
    return ;
}
void work()
{
    makesa();
    calcheight();
    build(rt1,1,n,false);build(rt2,1,n,true);
    ans=1;putchar('1');putchar('\n');
    update(rt1,1,n,rank[n]);
     
    for(int i=n-1;i>=1;i--)
    {
        t=-1;
        l=query(rt1,1,n,1,rank[i]-1,false);
        r=query(rt1,1,n,rank[i]+1,n,true);
        if(l<rank[i]&&l>0)t=max(t,query(rt2,1,n,l+1,rank[i],true));
        if(r>rank[i]&&r<=n)t=max(t,query(rt2,1,n,rank[i]+1,r,true));
        ans+=(long long)((n+1-i)-t);
        update(rt1,1,n,rank[i]);
        out(ans);
    }
    return ;
}
int main()
{
    //freopen("in.txt","r",stdin);
    Init(); work();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值