您的位置:首页 > 运维架构

【BZOJ4199】品酒大会,后缀数组+并查集维护

2016-05-23 22:11 330 查看
Time:2016.05.23

Author:xiaoyimi

转载注明出处谢谢

传送门

题面及样例

思路:

求lcp相关问题上有类似于差异的地方,但不完全是,”差异“所求的是lcp长度总和,而这里要对每个长度的lcp统计出现次数,更加精细

以前单调栈的做法不能满足我们了,只能另辟蹊径

(一开始我想的是线段树啥的……)

考虑这样一种思路,一开始每个后缀都是一个独立的集合,对于每个height[i],它可以把两个后缀集合x,y合并,那么对ans[height[i]]的贡献是siz[x]*siz[y],可以对最大乘积的贡献是max(这两个集合最大值的乘积,最小值的乘积)

用并查集维护每个集合的左端点,右端点,最小值和最大值

之后的事情就比较容易了

注意:

对于0相似的情况,个数肯定是n*(n-1)/2,最大值肯定是max(次大值最大值,最小值次小值)

代码:

#include<bits/stdc++.h>
#define M 300004
#define LL long long
using namespace std;
int len;
char s[M];
int minn[M],maxn[M],a[M],w[M],fa[M],sa[M],rank[M],cnt[M],tmp[M],id[M],L[M],R[M];
LL ans[2][M];
struct node
{
int data,id;
bool operator <(const node other)const
{
return data>other.data;
}
}height[M];
int in()
{
char ch=getchar();int t=0;bool f=0;
while (!isdigit(ch)) f=(ch=='-'),ch=getchar();
while (isdigit(ch)) t=(t<<1)+(t<<3)+ch-48,ch=getchar();
return f?-t:t;
}
void SA(int len,int up)
{
int *rk=rank,*t=tmp,d=1,p=0;
for (int i=0;i<len;i++) cnt[rk[i]=w[i]]++;
for (int i=1;i<up;i++) cnt[i]+=cnt[i-1];
for (int i=len-1;i>=0;i--) sa[--cnt[rk[i]]]=i;
for (;;)
{
for (int i=len-d;i<len;i++) id[p++]=i;
for (int i=0;i<len;i++)
if (sa[i]>=d) id[p++]=sa[i]-d;
for (int i=0;i<up;i++) cnt[i]=0;
for (int i=0;i<len;i++) cnt[t[i]=rk[id[i]]]++;
for (int i=1;i<up;i++) cnt[i]+=cnt[i-1];
for (int i=len-1;i>=0;i--) sa[--cnt[t[i]]]=id[i];
swap(t,rk);
p=1;
rk[sa[0]]=0;
for (int i=0;i<len-1;i++)
if (sa[i]+d<len&&sa[i+1]+d<len&&t[sa[i]]==t[sa[i+1]]&&t[sa[i]+d]==t[sa[i+1]+d])
rk[sa[i+1]]=p-1;
else
rk[sa[i+1]]=p++;
if (p==len) break;
d<<=1;up=p;p=0;
}
}
void Height(int len)
{
for (int i=1;i<=len;i++) rank[sa[i]]=i;
int k=0,x;
for (int i=0;i<len;i++)
{
k=max(k-1,0);
x=sa[rank[i]-1];
while (w[i+k]==w[x+k]) k++;
height[rank[i]]=(node){k,rank[i]};
}
}
int find(int x){return fa[x]=(x!=fa[x]?find(fa[x]):fa[x]);}
main()
{
len=in();
scanf("%s",s);
memset(ans[1],-127,sizeof(ans[1]));
for (int i=0;i<len;i++) a[i]=in();
for (int i=0;i<len;i++) w[i]=s[i]-'a'+1;
SA(len+1,28);
Height(len);
sort(height+1,height+len+1);
for (int i=1;i<=len;i++) fa[i]=i,L[i]=i,R[i]=i,minn[i]=maxn[i]=a[sa[i]];
for (int i=1;i<=len;i++)
if (!height[i].data) break;
else
{
int p=find(height[i].id),q=find(height[i].id-1);
ans[0][height[i].data]+=(LL)(R[p]-L[p]+1)*(R[q]-L[q]+1);
ans[1][height[i].data]=max((LL)minn[p]*minn[q],max(ans[1][height[i].data],(LL)maxn[p]*maxn[q]));
fa[p]=q;
L[q]=min(L[p],L[q]);
R[q]=max(R[p],R[q]);
minn[q]=min(minn[p],minn[q]);
maxn[q]=max(maxn[p],maxn[q]);
}
for (int i=len-1;i>=1;i--)
ans[0][i]+=ans[0][i+1],
ans[1][i]=max(ans[1][i],ans[1][i+1]);
sort(a,a+len);
printf("%lld %lld\n",(LL)len*(len-1)>>1,max((LL)a[1]*a[0],(LL)a[len-2]*a[len-1]));
for (int i=1;i<len;i++)
printf("%lld %lld\n",ans[0][i],!ans[0][i]?0:ans[1][i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: