维护集合两元素最大乘积

维护集合两元素最大乘积

给出多个集合,不断合并集合,要求求出最大集合中任意两个元素乘积的最大值

顾名思义,我们求最大值和次大值相乘一定最大

我们考虑到可能为负值,所以我们还需要维护最小值,和次小值

怎么维护呢?怎么把操作写的漂亮?

规定a序列为更新工具

维护b的最大值和次大值

    if(max1[a] >= max1[b]){//如果a的最大值大于等于b的最大值
                           //为啥是小于等于,因为我们需要顾及b的次大值是否需要更新
        max1[b] = max1[a];//b的最大值更新
        max2[b] = max(max1[b],max2[a]);//b的次大值只会存在于b的原最大值和a的次大值之中,毕竟默认的我们已知b的原最大值大于原次最大值
    }
    else if(max1[a] > max2[b])max2[b]=max1[a];//b的最大值不会被替代,那么只需要考虑b的次大值会不会被替代,而且a的最大值大于a的次大值,故只需要比较a的最大值和b的次大值的大小
    if(min1[a] <= min1[b]){最小值同理
        min1[b] = min1[a];
        min2[b] = min(min1[b],min2[a]);
        
    }
    else if(min1[a] < min2[b]) min2[b] = min1[a];
    maxx = max(maxx,max(max1[b] * max2[b],min1[b] * min2[b]));
    求一下最大乘积

应用

P2178 [NOI2015] 品酒大会

没错,这就是SA的题目,见博客适合于everyone的后缀数组

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<vector>
#define M 1000050
using namespace std;

int n,m,l;
const long long inf=2e20;
string s,ss;
long long w[M],siz[M],fa[M];
long long max1[M],max2[M],min1[M],min2[M];
int y[M],x[M],c[M],sa[M],rk[M],height[M],maxn=0;
long long ans[M][2];
vector<int>fam[M];

void get_sa(){
    for(int i=1;i<=n;++i) ++c[x[i]=s[i]];
    for(int i=2;i<=m;++i) c[i]+=c[i-1];
    for(int i=n;i>=1;--i) sa[c[x[i]]--]=i;
    for(int k=1;k<=n;k<<=1){
        int num=0;
        // for(int i=1;i<=num;i++) numm[i]=0;
        for(int i=n-k+1;i<=n;++i) y[++num]=i;
        for(int i=1;i<=n;++i) if(sa[i]>k) y[++num]=sa[i]-k;
        for(int i=1;i<=m;++i) c[i]=0;
        for(int i=1;i<=n;++i) maxn=max(maxn,++c[x[i]]);
        for(int i=2;i<=m;++i) c[i]+=c[i-1];
        for(int i=n;i>=1;--i) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        swap(x,y);
        x[sa[1]]=1;
        num=1;
        for(int i=2;i<=n;++i){
            if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]) {
                x[sa[i]]=num;
                // ++numm[num];
            }
            else {
                x[sa[i]]=++num;
            }
        }
        if(num==n)break;
        m=num;
    }

}

void get_lcp(){
    int k=0;
    for(int i=1;i<=n;++i)rk[sa[i]]=i;
    for(int i=1;i<=n;++i){
        if(rk[i]==1)continue;
        if(k)--k;
        int j=sa[rk[i]-1];
        while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])++k;
        height[rk[i]]=k;
    }
}

int find(int x){
    if(fa[x]!=x)fa[x]=find(fa[x]);
    return fa[x];
}

long long C(long long x){
    return (long long)1ll*x*(x-1)/2;
}

void val(int r){
    static long long cnt=0,maxx=-inf;
    for(auto x:fam[r]){
        int a=find(x-1),b=find(x);
        cnt-=C(siz[a])+C(siz[b]);
        fa[a]=b;
        siz[b]+=siz[a];
        cnt+=C(siz[b]);
        if(max1[a]>=max1[b]){
            max2[b]=max(max1[b],max2[a]);
            max1[b]=max1[a];
        }
        else if(max1[a]>max2[b])max2[b]=max1[a];
        if(min1[a]<=min1[b]){
            min2[b]=min(min1[b],min2[a]);
            min1[b]=min1[a];
        }
        else if(min1[a]<min2[b])min2[b]=min1[a];
        maxx=max(maxx,max(max1[b]*max2[b],min1[b]*min2[b]));
    }
    if(maxx==-inf)ans[r][0]=cnt,ans[r][1]=0;
    else ans[r][0]=cnt,ans[r][1]=maxx;
}

int main(){

    scanf("%d",&n);cin>>ss;
    l=ss.size();s='0';
    for(int i=1;i<=n;++i)cin>>w[i];
    for(int i=0;i<ss.size();i++)s+=ss[i];
    m=122;
    get_sa();
    get_lcp();
    for(int i=2;i<=n;++i)fam[height[i]].push_back(i);
    for(int i=1;i<=n;++i){
        fa[i]=i;siz[i]=1;
        max1[i]=min1[i]=w[sa[i]];/*1正2负*/
        max2[i]=-inf;min2[i]=inf;
    }
    for(int i=n-1;i>=0;--i)val(i);//倒序处理
    for(int i=0;i<n;++i) printf("%lld %lld \n",ans[i][0],ans[i][1]);
    fclose(stdin);
    fclose(stdout);
    return 0;
}



posted @ 2023-05-14 20:57  朝绾曦梦  阅读(43)  评论(0编辑  收藏  举报