zoukankan      html  css  js  c++  java
  • NOI2015 品酒大会

    先放个传送门

    后缀数组模板题

    如果(p,q)(r)相似的,相当于(p,q)有一个长度为(r)(lcp)
    考虑把(SA)(Height)建出来之后,让(Height)从大到小排序.然后从大到小做(因为两杯(r)相似的酒肯定是(0,1,2...r)相似的).用并查集维护每个连通块的(size,max,min).如果要合并(p,q),那么方案加上(size[p]*size[q]),(Ans=max(Ans,Max[p]*Max[q],Min[p]*Min[q]))即可.
    于是我们可以在(O(nlog n))的优秀复杂度内完成此题.

    代码如下(人傻自带大常数)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #define N (1000010)
    #define inf (0x7f7f7f7f)
    #define rg register int
    #define Label puts("NAIVE")
    #define spa print(' ')
    #define ent print('
    ')
    #define rand() (((rand())<<(15))^(rand()))
    typedef long double ld;
    typedef long long LL;
    typedef unsigned long long ull;
    using namespace std;
    struct Union{int x,y,pos;}a[N],b[N],c[N];
    struct edge{int l,r,v;}e[N];
    char s[N]; bool flag; LL ans,cnt,outa[N],outc[N];
    int n,suffix[N],rk[N],sa[N],rec[N],Height[N],M,w[N];
    int mx[N],siz[N],fa[N],mn[N];
    bool cmp(edge A,edge B){return A.v>B.v;}
    void jipai(){
        memset(rec,0,sizeof(rec));
        for(int i=1;i<=n;i++)rec[a[i].y]++;
        for(int i=1;i<=M;i++)rec[i]=rec[i-1]+rec[i];
        for(int i=n;i;i--)b[rec[a[i].y]]=a[i],rec[a[i].y]--;
        memset(rec,0,sizeof(rec));
        for(int i=1;i<=n;i++)rec[b[i].x]++;
        for(int i=1;i<=M;i++)rec[i]=rec[i-1]+rec[i];
        for(int i=n;i;i--)c[rec[b[i].x]]=b[i],rec[b[i].x]--;
        int p=0;
        for(int i=1;i<=n;i++){
            if(c[i].x!=c[i-1].x||c[i].y!=c[i-1].y)p++;
            rk[c[i].pos]=a[c[i].pos].x=p;
            if(p==n)flag=1; M=max(M,p);
        }
    }
    void get_sa(){
        scanf("%d%s",&n,s+1),M=127;
        for(int i=1;i<=n;i++)scanf("%d",&w[i]);
        for(int i=1;i<=n;i++)
        a[i].x=s[i]-'0'+1,a[i].y=0,a[i].pos=i;
        jipai();
        for(int i=1;i<=n;i++)a[i].x=rk[i];
        for(int k=1;k<=n;k*=2){
            for(int i=1;i<=n;i++)
            if(i+k>n)a[i].y=0;else a[i].y=rk[i+k];
            jipai();
            if(flag)break;
        }
        for(int i=1;i<=n;i++)sa[rk[i]]=i;
    }
    void get_height(){
        int x,y=0;
        for(int i=1;i<=n;Height[rk[i++]]=y)
        for(y=y?y-1:y,x=sa[rk[i]-1];s[i+y]==s[x+y];y++);
    }
    int ask(int x){
        return (fa[x]==x)?x:(fa[x]=ask(fa[x]));
    }
    void unite(int x,int y){
        int p=ask(x),q=ask(y);
        fa[q]=p,cnt+=((LL)siz[p]*(LL)siz[q]);
        ans=max(ans,max((LL)mx[p]*(LL)mx[q],(LL)mn[p]*(LL)mn[q]));
        mx[p]=max(mx[p],mx[q]),mn[p]=min(mn[p],mn[q]),siz[p]+=siz[q];
    }
    int main(){
        get_sa(),get_height();
        for(int i=1;i<=n;i++)fa[i]=i,mx[i]=mn[i]=w[i],siz[i]=1;
        for(int i=2;i<=n;i++)e[i-1]=(edge){sa[i-1],sa[i],Height[i]};
        sort(e+1,e+n,cmp),ans=-1e18-1;
        for(int i=n-1,j=1;i>=0;i--){
            for(;e[j].v==i;j++)unite(e[j].l,e[j].r);
            outa[i]=(cnt>0)?ans:0,outc[i]=cnt;
        }
        for(int i=0;i<n;i++)
        printf("%lld %lld
    ",outc[i],outa[i]);
    }
    
  • 相关阅读:
    poj 3068 Bridge Across Islands
    XidianOJ 1086 Flappy v8
    XidianOJ 1036 分配宝藏
    XidianOJ 1090 爬树的V8
    XidianOJ 1088 AK后的V8
    XidianOJ 1062 Black King Bar
    XidianOJ 1091 看Dota视频的V8
    XidianOJ 1098 突击数论前的xry111
    XidianOJ 1019 自然数的秘密
    XidianOJ 1109 Too Naive
  • 原文地址:https://www.cnblogs.com/Romeolong/p/10056786.html
Copyright © 2011-2022 走看看