[NOI2015]品酒大会

题目

洛谷
后缀数组一眼题系列

做法

\(r\)相似用\(height\)数组处理,每次按\(height\)分裂区间,显然是有单调性的

所求的\(r_0^{n-1}\)\(O(n^2)\)是过不了的
利用单调性,上烂大街的离线并查集
并查集要维护:大小,最大值,次大值,最小值,次小值(由于这里\(a_i\)为整数,所以答案可能由最小值与次小值贡献)

细节还是挺多的

#include<bits/stdc++.h>
#include<vector>
using namespace std;
typedef long long LL;
inline LL Read(){
    LL 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<<3)+(x<<1)+c-'0', c=getchar();
    return x*f;
}
const LL maxn=1e6+9,inf=(1e18+1);
LL n,ans,sum,va,m;
LL c[maxn],x[maxn],sa[maxn],y[maxn],size[maxn],fa[maxn],rk[maxn],hei[maxn],w[maxn],tmp[maxn][2];
LL mx[maxn],cx[maxn],mi[maxn],ci[maxn];
bool visit[maxn],vx[maxn],vi[maxn];
char s[maxn];
inline void Get_sa(){
    for(LL i=1;i<=n;++i) ++c[x[i]=(LL)(s[i]-'a'+1)];
    for(LL i=2;i<=m;++i) c[i]+=c[i-1];
    for(LL i=n;i>=1;--i) sa[c[x[i]]--]=i;
    for(LL len=1;len<=n;len<<=1){
        LL num(0);
        for(LL i=n-len+1;i<=n;++i) y[++num]=i;
        for(LL i=1;i<=n;++i) if(sa[i]>len) y[++num]=sa[i]-len;
        for(LL i=1;i<=m;++i) c[i]=0;
        for(LL i=1;i<=n;++i) ++c[x[i]];
        for(LL i=2;i<=m;++i) c[i]+=c[i-1];
        for(LL i=n;i>=1;--i) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        memcpy(y,x,sizeof(x));
        x[sa[1]]=num=1;
        for(LL i=2;i<=n;++i)
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+len]==y[sa[i-1]+len])?num:++num;
        if(num==n) break;
        m=num;
    }
    for(LL i=1;i<=n;++i) rk[sa[i]]=i;
    LL ret(0);
    for(LL i=1;i<=n;++i){
        if(ret) --ret;
        LL j(sa[rk[i]-1]);
        while(s[j+ret]==s[i+ret]) ++ret;
        hei[rk[i]]=ret;
    }
}
LL Get_f(LL x){ return fa[x]==x?x:fa[x]=Get_f(fa[x]); }
inline void Del(LL x){
    sum-=((size[x]-1)*size[x])/2;
}
inline void Merge(LL x,LL y){
    fa[x]=y;
    if(mx[x]>mx[y]){
        cx[y]=mx[y];
        mx[y]=mx[x];
        if(vx[x] && cx[x]>cx[y])
            cx[y]=cx[x];
    }else
        cx[y]=(vx[y]?max(cx[y],mx[x]):mx[x]);
    if(mi[x]<mi[y]){
        ci[y]=mi[y];
        mi[y]=mi[x];
        if(vi[x] && ci[x]<ci[y])
            ci[y]=ci[x];
    }else
        ci[y]=(vi[y]?min(ci[y],mi[x]):mi[x]);
    vx[y]=vi[y]=true;
    size[y]+=size[x];
    sum+=((size[y]-1)*size[y]>>1);
    ans=max(ans,max(mx[y]*cx[y],mi[y]*ci[y]));
}
vector<LL>mark[maxn];
int main(){
    n=Read();
    scanf(" %s",s+1);
    for(LL i=1;i<=n;++i) w[i]=Read();
    m=26; Get_sa();
    for(LL i=1;i<=n;++i) mark[hei[i]].push_back(i);
    for(LL i=1;i<=n;++i) fa[i]=i;
    for(LL i=1;i<=n;++i){
        mx[i]=mi[i]=w[i];
        vx[i]=vi[i]=false;
    }
    ans=-inf;
    for(LL i=1;i<=n;++i) size[i]=1;
    for(LL i=n-1;i>=0;--i){
        for(LL j=0;j<mark[i].size();++j){
            LL x(sa[mark[i][j]]);
            visit[x]=true;
            LL sax(rk[x]), nxt(sa[sax+1]),pre(sa[sax-1]);
            LL fy(Get_f(pre));
            Del(x); Del(fy);
            Merge(x,fy);
        }
        tmp[i][0]=sum; tmp[i][1]=(ans<=-inf?0:ans);
    }
    for(LL i=0;i<n;++i) printf("%lld %lld\n",tmp[i][0],tmp[i][1]);
    return 0;
}
posted @ 2019-03-12 10:50  y2823774827y  阅读(177)  评论(0编辑  收藏  举报