zoukankan      html  css  js  c++  java
  • BZOJ4516 SDOI2016生成魔咒(后缀数组+平衡树)

      一个字符串本质不同的子串数量显然是总子串数减去所有height值。如果一个个往里加字符的话,每次都会改动所有后缀完全没法做。但发现如果从后往前加的话,每次只会添加一个后缀。于是我们把字符串倒过来,每次往里添加后缀并维护答案。可以用一棵平衡树,每次插入时查询这个名次的前驱后继以更新。

      SA板子敲得磕磕绊绊,没什么救了。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<set>
    using namespace std;
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    #define N 100010
    int n,a[N],b[N],cnt[N],sa[N],sa2[N],tmp[N<<1],rk[N<<1];
    int h[N],s[N],f[N][19],lg2[N];
    set<int> tree;
    int query(int x,int y)
    {
        if (x>y) swap(x,y);
        y--;
        return min(f[x][lg2[y-x+1]],f[y-(1<<lg2[y-x+1])+1][lg2[y-x+1]]);
    }
    void make(int m)
    {
        memset(cnt,0,sizeof(cnt));
        for (int i=1;i<=n;i++) cnt[rk[i]=a[i]]++;
        for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
        for (int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
        for (int k=1;k<=n;k<<=1)
        {
            int p=0;
            for (int i=n-k+1;i<=n;i++) sa2[++p]=i;
            for (int i=1;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;
            memset(cnt,0,sizeof(cnt));
            for (int i=1;i<=n;i++) cnt[rk[i]]++;
            for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
            for (int i=n;i>=1;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];
            memcpy(tmp,rk,sizeof(rk));
            p=1;rk[sa[1]]=1;
            for (int i=2;i<=n;i++)
            {
                if (tmp[sa[i]]!=tmp[sa[i-1]]||tmp[sa[i]+k]!=tmp[sa[i-1]+k]) p++;
                rk[sa[i]]=p;
            }
            if (p>=n) break;
            m=p;
        }
        for (int i=1;i<=n;i++)
        {
            h[i]=max(h[i-1]-1,0);
            while (a[i+h[i]]==a[sa[rk[i]-1]+h[i]]) h[i]++;
        }
        for (int i=1;i<n;i++) f[i][0]=h[sa[i+1]];
        for (int j=1;j<19;j++)
            for (int i=1;i<n;i++) 
            f[i][j]=min(f[i][j-1],f[min(n-1,i+(1<<j-1))][j-1]);
        lg2[1]=0;
        for (int i=1;i<n;i++)
        {
            lg2[i]=lg2[i-1];
            if ((2<<lg2[i])<=i) lg2[i]++;
        }
    }
    int main()
    {
        freopen("bzoj4516.in","r",stdin);
        freopen("bzoj4516.out","w",stdout);
        n=read();
        for (int i=1;i<=n;i++) b[i]=a[i]=read();
        sort(b+1,b+n+1);
        int t=unique(b+1,b+n+1)-b;
        for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+t,a[i])-b;
        reverse(a+1,a+n+1);
        make(t);
        tree.clear();tree.insert(rk[n]);
        long long ans=1;cout<<ans<<endl;
        for (int i=n-1;i>=1;i--)
        {
            set<int>::iterator it=tree.lower_bound(rk[i]);
            ans+=n-i+1;
            if (it==tree.begin()) ans-=query(rk[i],*it);
            else if (it==tree.end()) ans-=query(rk[i],*(--it));
            else
            {
                int x=*it,y=*(--it);
                ans+=query(x,y);
                ans-=query(x,rk[i]),ans-=query(y,rk[i]);
            }
            tree.insert(rk[i]);
            printf("%lld
    ",ans);
        }
        fclose(stdin);fclose(stdout);
        return 0;
    }
  • 相关阅读:
    javascript预编译练习(变态篇)
    javascript预编译
    原码、反码、补码 详解
    Ubuntu 16.04安装anaconda3
    html+css实现奥运五环(环环相扣)
    jquery实现分页效果
    Centos6.5修改mysql登陆用户密码
    2020/5/29 JS中的循环和函数
    2020/5/27 JS 循环语句
    2020/5/26 JS
  • 原文地址:https://www.cnblogs.com/Gloid/p/9394417.html
Copyright © 2011-2022 走看看