NOI2015 品酒大会
先放个传送门
后缀数组模板题
如果\(p,q\)是\(r\)相似的,相当于\(p,q\)有一个长度为\(r\)的\(lcp\)
考虑把\(SA\)和\(Height\)建出来之后,让\(Height\)从大到小排序.然后从大到小做(因为两杯\(r\)相似的酒肯定是\(0,1,2...r\)相似的).用并查集维护每个连通块的\(size,max,min\).如果要合并\(p,q\),那么方案加上\(size[p]*size[q]\),\(Ans=max(Ans,Max[p]*Max[q],Min[p]*Min[q])\)即可.
于是我们可以在\(O(n\log n)\)的优秀复杂度内完成此题.
代码如下(人傻自带大常数)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N (1000010)
#define inf (0x7f7f7f7f)
#define rg register int
#define Label puts("NAIVE")
#define spa print(' ')
#define ent print('\n')
#define rand() (((rand())<<(15))^(rand()))
typedef long double ld;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
struct Union{int x,y,pos;}a[N],b[N],c[N];
struct edge{int l,r,v;}e[N];
char s[N]; bool flag; LL ans,cnt,outa[N],outc[N];
int n,suffix[N],rk[N],sa[N],rec[N],Height[N],M,w[N];
int mx[N],siz[N],fa[N],mn[N];
bool cmp(edge A,edge B){return A.v>B.v;}
void jipai(){
memset(rec,0,sizeof(rec));
for(int i=1;i<=n;i++)rec[a[i].y]++;
for(int i=1;i<=M;i++)rec[i]=rec[i-1]+rec[i];
for(int i=n;i;i--)b[rec[a[i].y]]=a[i],rec[a[i].y]--;
memset(rec,0,sizeof(rec));
for(int i=1;i<=n;i++)rec[b[i].x]++;
for(int i=1;i<=M;i++)rec[i]=rec[i-1]+rec[i];
for(int i=n;i;i--)c[rec[b[i].x]]=b[i],rec[b[i].x]--;
int p=0;
for(int i=1;i<=n;i++){
if(c[i].x!=c[i-1].x||c[i].y!=c[i-1].y)p++;
rk[c[i].pos]=a[c[i].pos].x=p;
if(p==n)flag=1; M=max(M,p);
}
}
void get_sa(){
scanf("%d%s",&n,s+1),M=127;
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=1;i<=n;i++)
a[i].x=s[i]-'0'+1,a[i].y=0,a[i].pos=i;
jipai();
for(int i=1;i<=n;i++)a[i].x=rk[i];
for(int k=1;k<=n;k*=2){
for(int i=1;i<=n;i++)
if(i+k>n)a[i].y=0;else a[i].y=rk[i+k];
jipai();
if(flag)break;
}
for(int i=1;i<=n;i++)sa[rk[i]]=i;
}
void get_height(){
int x,y=0;
for(int i=1;i<=n;Height[rk[i++]]=y)
for(y=y?y-1:y,x=sa[rk[i]-1];s[i+y]==s[x+y];y++);
}
int ask(int x){
return (fa[x]==x)?x:(fa[x]=ask(fa[x]));
}
void unite(int x,int y){
int p=ask(x),q=ask(y);
fa[q]=p,cnt+=((LL)siz[p]*(LL)siz[q]);
ans=max(ans,max((LL)mx[p]*(LL)mx[q],(LL)mn[p]*(LL)mn[q]));
mx[p]=max(mx[p],mx[q]),mn[p]=min(mn[p],mn[q]),siz[p]+=siz[q];
}
int main(){
get_sa(),get_height();
for(int i=1;i<=n;i++)fa[i]=i,mx[i]=mn[i]=w[i],siz[i]=1;
for(int i=2;i<=n;i++)e[i-1]=(edge){sa[i-1],sa[i],Height[i]};
sort(e+1,e+n,cmp),ans=-1e18-1;
for(int i=n-1,j=1;i>=0;i--){
for(;e[j].v==i;j++)unite(e[j].l,e[j].r);
outa[i]=(cnt>0)?ans:0,outc[i]=cnt;
}
for(int i=0;i<n;i++)
printf("%lld %lld\n",outc[i],outa[i]);
}