[NOI2015] 品酒大会 - 后缀数组,并查集,STL,启发式合并

[NOI2015] 品酒大会

Description

对于每一个 \(i \in [0,n)\) 求有多少对后缀满足 LCP 长度 \(\le i\) ,并求满足条件的两个后缀权值乘积的最大值。

Solution

很容易想到并查集,将 \(i\) 从大到小处理,每到一个新的 \(i\) ,就将所有 \(h[j]=i\)\(j-1\)\(j\) 两个后缀所在集合合并,维护每个集合的大小以及其中最最大次大最小次小。注意判断一下边界情况。

但是我非常懒惰,所以用了 set + 启发式合并。

// How many different substrings are there in the main string
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,m=256,sa[1000005],y[1000005],u[1000005],v[1000005],o[1000005];
int r[1000005],h[1000005],T,val[1000005],f[300005];
char str[1000005];
long long ans1,ans2=-1e18;
vector <int> pos[300005];
multiset <int> st[300005];

int mix(multiset <int> &a, multiset <int> &b)
{
    if(a.size()>b.size())
    {
        a.insert(b.begin(),b.end());
        return 0;
    }
    else
    {
        b.insert(a.begin(),a.end());
        return 1;
    }
}

int getans2(multiset <int> &s)
{
    if(s.size()<2) return 0;
    else
    {
        multiset<int>::iterator it1,it2,it3,it4;
        it1=s.begin();
        it2=it1;
        ++it2;
        it3=s.end();
        --it3;
        it4=it3;
        --it4;
        return max((*it1)*(*it2), (*it3)*(*it4));
    }
}

int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
void merge(int i,int j)
{
    if(find(i)!=find(j))
    {
        ans1+=(int)(st[find(i)].size())*(int)(st[find(j)].size());
        int fg=mix(st[find(i)],st[find(j)]);
        if(fg) f[find(i)]=find(j);
        else f[find(j)]=find(i);
        ans2=max(ans2,getans2(st[find(i)]));
    }
}

signed main()
{
    scanf("%lld",&n);
    scanf("%s",str+1);
    for(int i=1; i<=n; i++) scanf("%lld",&val[i]);

    for(int i=1; i<=n; i++) u[str[i]]++;
    for(int i=1; i<=m; i++) u[i]+=u[i-1];
    for(int i=n; i>=1; i--) sa[u[str[i]]--]=i;
    r[sa[1]]=1;
    for(int i=2; i<=n; i++) r[sa[i]]=r[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);

    for(int l=1; r[sa[n]]<n; l<<=1)
    {
        memset(u,0,sizeof u);
        memset(v,0,sizeof v);
        memcpy(o,r,sizeof r);
        for(int i=1; i<=n; i++) u[r[i]]++, v[r[i+l]]++;
        for(int i=1; i<=n; i++) u[i]+=u[i-1], v[i]+=v[i-1];
        for(int i=n; i>=1; i--) y[v[r[i+l]]--]=i;
        for(int i=n; i>=1; i--) sa[u[r[y[i]]]--]=y[i];
        r[sa[1]]=1;
        for(int i=2; i<=n; i++) r[sa[i]]=r[sa[i-1]]+((o[sa[i]]!=o[sa[i-1]])||(o[sa[i]+l]!=o[sa[i-1]+l]));
    }
    {
        int i,j,k=0;
        for(int i=1; i<=n; h[r[i++]]=k)
            for(k?k--:0,j=sa[r[i]-1]; str[i+k]==str[j+k]; k++);
    }

    for(int i=2; i<=n; i++)
    {
        pos[h[i]].push_back(i);
    }
    for(int i=1; i<=n; i++) st[i].insert(val[sa[i]]);
    for(int i=1; i<=n; i++) f[i]=i;
    vector <int> a1,a2;
    for(int i=n-1; i>=0; --i)
    {
        for(int j=0; j<pos[i].size(); j++)
        {
            merge(pos[i][j],pos[i][j]-1);
        }
        a1.push_back(ans1);
        a2.push_back(ans2);
    }
    for(int i=n-1; i>=0; --i) printf("%lld %lld\n",a1[i],(a2[i]<-9e17?0:a2[i]));
}

猜你喜欢

转载自www.cnblogs.com/mollnn/p/11762266.html