您的位置:首页 > 其它

bzoj 4199: [Noi2015]品酒大会 后缀数组+并查集

2017-04-11 15:23 435 查看

题意

给出一个 长度为 n 的字符串,每一位有一个权值 val。定义两个位字符为 r 相似,是指分别从这两个字符开始,到后面的 r 个字符都相等。两个 r 相似的字符还有一个权值为这两个字符权值的乘积。问对于 r = 0, 1, 2, … , n - 1,统计出有多少种方法可以选出 2 个“r 相似”的字符,并回答选择 2 个”r 相似”的字符可以得到的权值的最大值。

n<=3*10^5,val <=10^9

分析

先构造出height数组,然后把height数组从大到小排序,并逐个加入。每加入一个就用并查集维护一下数量,最大值次大值,最小值次小值并计算一波贡献即可。

一开始用vector来排序height数组结果T了,改成排序才过。。。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

const int N=300005;
const int inf=0x7fffffff;

int n,b
,c
,d
,rank[N*2],sa
,height
,f
,size
,mx1
,mx2
,mn1
,mn2
,a
,num
;
LL ans
,mx
;
char s
;

void get_sa(int n,int m)
{
for (int i=1;i<=n;i++) b[s[i]]++;
for (int i=1;i<=m;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) c[b[s[i]]--]=i;
int t=0;
for (int i=1;i<=n;i++)
{
if (s[c[i]]!=s[c[i-1]]) t++;
rank[c[i]]=t;
}
int j=1;
while (j<=n)
{
for (int i=1;i<=n;i++) b[i]=0;
for (int i=1;i<=n;i++) b[rank[i+j]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;
for (int i=1;i<=n;i++) b[i]=0;
for (int i=1;i<=n;i++) b[rank[i]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];
t=0;
for (int i=1;i<=n;i++)
{
if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) t++;
c[d[i]]=t;
}
for (int i=1;i<=n;i++) rank[i]=c[i];
if (t==n) break;
j*=2;
}
for (int i=1;i<=n;i++) sa[rank[i]]=i;
}

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

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

LL calc(int x)
{
return (LL)size[x]*(size[x]-1)/2;
}

void merge(int x,int y)
{
size[y]+=size[x];f[x]=y;
if (mx1[x]!=-inf)
{
int w=mx1[x];
if (w>mx1[y]) mx2[y]=mx1[y],mx1[y]=w;
else if (w>mx2[y]) mx2[y]=w;
}
if (mx2[x]!=-inf)
{
int w=mx2[x];
if (w>mx1[y]) mx2[y]=mx1[y],mx1[y]=w;
else if (w>mx2[y]) mx2[y]=w;
}
if (mn1[x]!=inf)
{
int w=mn1[x];
if (w<mn1[y]) mn2[y]=mn1[y],mn1[y]=w;
else if (w<mn2[y]) mn2[y]=w;
}
if (mn2[x]!=inf)
{
int w=mn2[x];
if (w<mn1[y]) mn2[y]=mn1[y],mn1[y]=w;
else if (w<mn2[y]) mn2[y]=w;
}
}

bool cmp(int a,int b)
{
return height[a]>height[b];
}

int main()
{
scanf("%d",&n);
scanf("%s",s+1);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
get_sa(n,200);
get_height(n);
for (int i=1;i<n;i++) num[i]=i+1;
sort(num+1,num+n,cmp);
for (int i=1;i<=n;i++) f[i]=i,mn1[i]=mx1[i]=a[sa[i]],mn2[i]=inf,mx2[i]=-inf,size[i]=1;
for (int i=0;i<=n;i++) mx[i]=-(LL)inf*inf;
int p=1;
for (int i=n-1;i>=0;i--)
{
ans[i]=ans[i+1];mx[i]=mx[i+1];
while (height[num[p]]==i&&p<n)
{
int x=num[p];
ans[i]-=calc(find(x))+calc(find(x-1));
merge(find(x),find(x-1));
int w=find(x);
ans[i]+=calc(w);
mx[i]=max(mx[i],max((LL)mx1[w]*mx2[w],(LL)mn1[w]*mn2[w]));
p++;
}
}
for (int i=0;i<=n-1;i++) printf("%lld %lld\n",ans[i],mx[i]==-(LL)inf*inf?0:mx[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: