bzoj4199 [Noi2015]品酒大会
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4199
题意:
(Rinbow和Freda日常虐狗 。)
在大会的晚餐上,调酒师 Rainbow 调制了 n 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 i 杯酒 (1≤i≤n1≤i≤n) 被贴上了一个标签 si,每个标签都是 26 个小写英文字母之一。设 Str(l,r) 表示第 l 杯酒到第 r 杯酒的 r−l+1 个标签顺次连接构成的字符串。若 Str(p,po)=Str(q,qo),其中 1≤p≤po≤n,1≤q≤qo≤n,p≠q,po−p+1=qo−q+1=r,则称第 p 杯酒与第 q 杯酒是“r相似” 的。当然两杯“r相似” (r>1r>1)的酒同时也是“1 相似”、“2 相似”、……、“(r−1) 相似”的。特别地,对于任意的 1≤p,q≤n,p≠q,第 p 杯酒和第 q 杯酒都是“0相似”的。
在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 i 杯酒 (1≤i≤n) 的美味度为 ai。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 p 杯酒与第 q 杯酒调兑在一起,将得到一杯美味度为 ap*aq 的酒。现在请各位品酒师分别对于 r=0,1,2,…,n−1,统计出有多少种方法可以选出 2 杯“r相似”的酒,并回答选择 2 杯“r相似”的酒调兑可以得到的美味度的最大值。
数据范围
n<=300000,∣ai∣≤1000000000
题解:
水题写一天系列。
题意即给一个字符串S,令Si表示字符串S从i位置起始的的后缀。定义i和j是r相似的,仅当 Si, Sj存在长度为r的公共前缀。(r相似的必然也是 r-1,r-2…0相似的)每个后缀Si有一个价值ai,定义一对后缀价值两个后缀的价值之积。求对于每个r(0~n-1),有多少对r相似的后缀,和任取一对r相似的后缀的最大价值。
SAM:
就是把原串倒过来,那么两个后缀的公共前缀变成了两个前缀的公共后缀,就是parent树的lca,
建出parent树,那么一个点作为lca的贡献就是不同子树中可作为前缀的节点的个数(就是不是nB新建的节点)乘一乘,
求最大值就同时维护个最大值&最小值,乘一乘即可。
SA:
1、ST表
我的想法简单粗暴,
既然两个后缀的lcp是这个区间内height值的最小值,那么就直接看每个后缀的height能贡献到多大的区间。
用单调栈左边找第一个<=它的+1,右边找第一个<它的-1,就找到了这个区间。
(细节挺多。这里自己理了很久。
这里之所以要这样做是因为height有相同的,
同一对点被两个位置枚举到当且仅当有height相等的挨着,为此才设计成一边<=,一边<。
同时因为两个后缀i,j的lcp其实是
[ height[sa[i]+1],height[sa[j]] ]
这个区间的最小值,
因此对于
sa[x]=i
找到的区间
l[i]
,
r[i]
,其贡献
(i−l[i]+1)∗(r[i]−i+1)
也是计算了
l[i]−1
这个后缀。
也因此,如果
l[i]==1
,没有
l[i]−1
这个后缀 就不是上述贡献了。
为了少管一点细节,利用了
hetght[1]=0
,并且把<=设计在左边,就不会有
l[i]==1
了。)
然后对于第二问,st表查一查 [ l[i]−1,i−1] 和 [ i,r[i] ] 的最小最大值大大小小乘一乘即可。
2、并查集
正解代码短常数小跑得飞快真是妙妙。
正解是这样想的,单独求一个r,可以按height分组,这一组中可以任选两个,
而要求所有的r,考虑r从大到小,那么分组回越来越少,不同的组回合成一个组。
就仿佛是最初互不联通的一些点,在sa[i]和sa[i-1]间有权值为height[i]的边,
随着r从大到小,把满足要求的边加进去,当时联通的点中任选两个构成当前r的答案。
这就是一个并查集合并的过程。按height从大到小排序,依次合并,并查集维护下size最大最小值即可。
注意:
1.a[i]有正有负,所以最大最小值都要维护。
2.小心有些0-1=-1数组越界的情况。
两个代码都在下面了。
单调栈+st表 代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=300005;
const int inf=0x3f3f3f3f;
const int P=18;
int sa[N],rk[N],a[N],b[N],w[N],ht[N],mn[N][P+1],mx[N][P+1],val[N],l[N],r[N],stack[N],top=0,lg[N];
LL ans1[N],ans2[N];
char s[N];
inline int read()
{
int ret=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-'){w=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar();}
return ret*w;
}
struct node
{
int h,x;
node(){}
node(int h,int x):h(h),x(x){}
}Q[N];
bool cmp(const node &A,const node &B){return A.h!=B.h?A.h>B.h:A.x>B.x;}
void SA(int n,int m)
{
int *x=a; int *y=b;
for(int i=0;i<=n;i++) w[x[i]=s[i]]++;
for(int i=1;i<=m;i++) w[i]+=w[i-1];
for(int i=n;i>=0;i--) sa[--w[x[i]]]=i;
for(int i=0;i<=m;i++) w[i]=0;
for(int l=1,p=0;l<=n;l<<=1)
{
for(int i=n-l+1;i<=n;i++) y[p++]=i;
for(int i=0;i<=n;i++) if(sa[i]>=l) y[p++]=sa[i]-l;
for(int i=0;i<=n;i++) w[x[i]]++;
for(int i=1;i<=m;i++) w[i]+=w[i-1];
for(int i=n;i>=0;i--) sa[--w[x[y[i]]]]=y[i];
for(int i=0;i<=m;i++) w[i]=0;
swap(x,y); p=1; x[sa[0]]=0;
for(int i=1;i<=n;i++) x[sa[i]]= (y[sa[i]]==y[sa[i-1]]&&y[sa[i]+l]==y[sa[i-1]+l])?p-1:p++;
m=p; p=0; if(p>n) break;
}
for(int i=0;i<=n;i++) rk[sa[i]]=i; int k=0;
for(int i=0;i<n;i++)
{
if(k) k--; int j=sa[rk[i]-1];
while(s[i+k]==s[j+k]) k++;
ht[rk[i]]=k;
}
}
inline int qmax(int L,int R)
{
return max(mx[L][lg[R-L+1]],mx[R-(1<<lg[R-L+1])+1][lg[R-L+1]]);
}
inline int qmin(int L,int R)
{
return min(mn[L][lg[R-L+1]],mn[R-(1<<lg[R-L+1])+1][lg[R-L+1]]);
}
void build(int n)
{
lg[1]=0; for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++) mn[i][0]=mx[i][0]=val[i];
for(int p=1;p<=P;p++)
{
for(int i=1;i<=n;i++)
{
mx[i][p]=mx[i][p-1]; mn[i][p]=mn[i][p-1];
if(i+(1<<(p-1))<=n) mx[i][p]=max(mx[i][p],mx[i+(1<<(p-1))][p-1]);
if(i+(1<<(p-1))<=n) mn[i][p]=min(mn[i][p],mn[i+(1<<(p-1))][p-1]);
}
}
top=0;
for(int i=1;i<=n;i++)
{
while(top&&ht[stack[top]]>ht[i]) top--;
l[i]=top?stack[top]+1:1;
stack[++top]=i;
}top=0;
for(int i=n;i>=1;i--)
{
while(top&&ht[stack[top]]>=ht[i]) top--;
r[i]=top?stack[top]-1:n;
stack[++top]=i;
}
for(int i=2;i<=n;i++)
{
ans1[ht[i]]+=1LL*(i-l[i]+1)*(r[i]-i+1);
int mxl=qmax(l[i]-1,i-1); int mxr=qmax(i,r[i]);
int mnl=qmin(l[i]-1,i-1); int mnr=qmin(i,r[i]);
ans2[ht[i]]=max(ans2[ht[i]],max(1LL*mxl*mxr,1LL*mnl*mnr));
}
}
int main()
{
int n=read(); scanf("%s",s); s[n]=0;
SA(n,128);
for(int i=0;i<n;i++) {val[rk[i]]=read(); ans1[i]=0; ans2[i]=-1LL*inf*inf;}
build(n);
for(int i=n-2;i>=0;i--) {ans1[i]+=ans1[i+1]; ans2[i]=max(ans2[i+1],ans2[i]);}
for(int i=0;i<n;i++)
{
printf("%lld ",ans1[i]);
if(ans1[i]) printf("%lld\n",ans2[i]); else printf("0\n");
}
return 0;
}
并查集 代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=300005;
const int inf=0x3f3f3f3f;
int sa[N],rk[N],a[N],b[N],w[N],ht[N],fa[N],mn[N],mx[N],val[N],sz[N];
LL ans1[N],ans2[N];
char s[N];
inline int read()
{
int ret=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-'){w=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar();}
return ret*w;
}
struct node
{
int h,x;
node(){}
node(int h,int x):h(h),x(x){}
}Q[N];
bool cmp(const node &A,const node &B){return A.h!=B.h?A.h>B.h:A.x>B.x;}
void SA(int n,int m)
{
int *x=a; int *y=b;
for(int i=0;i<=n;i++) w[x[i]=s[i]]++;
for(int i=1;i<=m;i++) w[i]+=w[i-1];
for(int i=n;i>=0;i--) sa[--w[x[i]]]=i;
for(int i=0;i<=m;i++) w[i]=0;
for(int l=1,p=0;l<=n;l<<=1)
{
for(int i=n-l+1;i<=n;i++) y[p++]=i;
for(int i=0;i<=n;i++) if(sa[i]>=l) y[p++]=sa[i]-l;
for(int i=0;i<=n;i++) w[x[i]]++;
for(int i=1;i<=m;i++) w[i]+=w[i-1];
for(int i=n;i>=0;i--) sa[--w[x[y[i]]]]=y[i];
for(int i=0;i<=m;i++) w[i]=0;
swap(x,y); p=1; x[sa[0]]=0;
for(int i=1;i<=n;i++) x[sa[i]]= (y[sa[i]]==y[sa[i-1]]&&y[sa[i]+l]==y[sa[i-1]+l])?p-1:p++;
m=p; p=0; if(p>n) break;
}
for(int i=0;i<=n;i++) rk[sa[i]]=i; int k=0;
for(int i=0;i<n;i++)
{
if(k) k--; int j=sa[rk[i]-1];
while(s[i+k]==s[j+k]) k++;
ht[rk[i]]=k;
}
}
int getfa(int x)
{
if(fa[x]==x) return x;
else return fa[x]=getfa(fa[x]);
}
int main()
{
int n=read(); scanf("%s",s); s[n]=0;
SA(n,128);
for(int i=0;i<n;i++) {val[rk[i]]=read(); ans1[i]=0; ans2[i]=-1LL*inf*inf;}
for(int i=1;i<=n;i++) {mx[i]=mn[i]=val[i]; fa[i]=i; sz[i]=1; Q[i]=node(ht[i],i);}
sort(Q+1,Q+n+1,cmp);
for(int i=1;i<n;i++) //第n个是sa[1] y=0
{
int x=getfa(Q[i].x); int y=getfa(Q[i].x-1);
ans1[Q[i].h]+=1LL*sz[x]*sz[y];
ans2[Q[i].h]=max(ans2[Q[i].h],max(1LL*mx[x]*mx[y],1LL*mn[x]*mn[y]));
fa[y]=x; mn[x]=min(mn[x],mn[y]); mx[x]=max(mx[x],mx[y]); sz[x]+=sz[y];
}
for(int i=n-2;i>=0;i--) {ans1[i]+=ans1[i+1]; ans2[i]=max(ans2[i+1],ans2[i]);}
for(int i=0;i<n;i++)
{
printf("%lld ",ans1[i]);
if(ans1[i]) printf("%lld\n",ans2[i]); else printf("0\n");
}
return 0;
}