维护集合两元素最大乘积
维护集合两元素最大乘积
给出多个集合,不断合并集合,要求求出最大集合中任意两个元素乘积的最大值
顾名思义,我们求最大值和次大值相乘一定最大
我们考虑到可能为负值,所以我们还需要维护最小值,和次小值
怎么维护呢?怎么把操作写的漂亮?
规定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]));
求一下最大乘积
应用
没错,这就是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;
}