[NOI2015]品酒大会

[NOI2015]品酒大会

题目大意:

一个长度为\(n(n\le3\times10^5)\)的字符串\(s(s_i\in[\text{'a'},'z'])\),若对于后缀\(i,j\)\(\operatorname{lcp}(i,j)\le r\),则我们称后缀对\((i,j)\)为「\(r\)相似」的。另有数列\(a_{1\sim n}\),表示后缀对\((i,j)\)的贡献为\(a_i\cdot a_j\)。对于\(r=0,1,\ldots,n-1\),求「\(r\)相似」的后缀对有多少组,其中贡献的最大值为多少。

思路:

首先用后缀数组求出字符串\(s\)\(lcp\)数组。对于\(lcp\)数组排序,按从大到小顺序合并\(lcp=i\)对应的两个后缀所属的联通块,合并后块内任意两个后缀的\(\operatorname{lcp}\)一定\(\ge r\),即一定为「\(r\)相似」的。对于每个连通块,维护\(a_i\)的最大值、次大值、最小值、次小值即可求得两数之积的最大值。对于方案数,可以简单地求出合并后各连通块内两两组合的方案数之和。

使用\(O(n\log^2n)\)的后缀数组可以在洛谷、LOJ、UOJ上通过,BZOJ会被卡,需要使用更优秀的算法。

源代码:

#include<cstdio>
#include<cctype>
#include<vector>
#include<climits>
#include<algorithm>
#include<functional>
typedef long long int64;
inline int getint() {
    register char ch;
    register bool neg=false;
    while(!isdigit(ch=getchar())) neg|=ch=='-';
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return neg?-x:x;
}
const int N=3e5+1;
char s[N];
int n,k,sa[N],rank[N],tmp[N];
inline bool cmp(const int &i,const int &j) {
    if(rank[i]!=rank[j]) return rank[i]<rank[j];
    const int ri=i+k<=n?rank[i+k]:-1;
    const int rj=j+k<=n?rank[j+k]:-1;
    return ri<rj;
}
inline void suffix_sort() {
    for(register int i=0;i<=n;i++) {
        sa[i]=i;
        rank[i]=s[i];
    }
    for(k=1;k<=n;k<<=1) {
        std::sort(&sa[0],&sa[n]+1,cmp);
        tmp[sa[0]]=0;
        for(register int i=1;i<=n;i++) {
            tmp[sa[i]]=tmp[sa[i-1]]+cmp(sa[i-1],sa[i]);
        }
        std::copy(&tmp[0],&tmp[n]+1,rank);
    }
}
struct Node {
    int val,id;
    bool operator > (const Node &rhs) const {
        return val>rhs.val;
    }
};
Node lcp[N];
inline void init_lcp() {
    for(register int i=0,h=0;i<n;i++) {
        if(h>0) h--;
        const int &j=sa[rank[i]-1];
        while(i+h<n&&j+h<n&&s[i+h]==s[j+h]) h++;
        lcp[i]=(Node){h,i};
    }
}
int m[2][2][N];
int64 ans[N],cnt[N];
struct DisjointSet {
    int anc[N],size[N];
    void reset(const int &n) {
        for(register int i=1;i<=n;i++) {
            anc[i]=i;
            size[i]=1;
        }
    }
    int find(const int &x) {
        return x==anc[x]?x:anc[x]=find(anc[x]);
    }
};
DisjointSet djs;
int main() {
    n=getint();
    scanf("%s",s);
    for(register int i=1;i<=n;i++) {
        m[0][0][i]=m[1][0][i]=getint();
        m[0][1][i]=INT_MAX;
        m[1][1][i]=INT_MIN;
    }
    suffix_sort();
    init_lcp();
    std::sort(&lcp[0],&lcp[n],std::greater<Node>());
    djs.reset(n);
    ans[n]=LLONG_MIN;
    for(register int i=n-1,j=0;~i;i--) {
        cnt[i]=cnt[i+1];
        ans[i]=ans[i+1];
        for(;j<n&&lcp[j].val>=i;j++) {
            const int u=djs.find(lcp[j].id+1);
            const int v=djs.find(sa[rank[lcp[j].id]-1]+1);
            if(u==v||u==0||v==0) continue;
            djs.anc[u]=v;
            cnt[i]-=(int64)djs.size[u]*(djs.size[u]-1)/2+(int64)djs.size[v]*(djs.size[v]-1)/2;
            djs.size[v]+=djs.size[u];
            cnt[i]+=(int64)djs.size[v]*(djs.size[v]-1)/2;
            for(register int i=0;i<2;i++) {
                for(register int j=i;j<2;j++) {
                    if(m[0][i][u]<m[0][j][v]) std::swap(m[0][i][u],m[0][j][v]);
                    if(m[1][i][u]>m[1][j][v]) std::swap(m[1][i][u],m[1][j][v]);
                }
            }
            ans[i]=std::max(ans[i],std::max((int64)m[0][0][v]*m[0][1][v],(int64)m[1][0][v]*m[1][1][v]));
        }
    }
    for(register int i=0;i<n;i++) {
        printf("%lld %lld\n",cnt[i],ans[i]==LLONG_MIN?0:ans[i]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/skylee03/p/9211277.html