4199: [Noi2015]品酒大会
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 418 Solved: 231
[Submit][Status][Discuss]
Description
一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。
在大会的晚餐上,调酒师
Rainbow
调制了
n
杯鸡尾酒。这
在品尝环节上,品酒师
Input
输入文件的第
1
行包含
第
第
3
行包含
Output
输出文件包括
n
行。第
Sample Input
10
ponoiiipoi
2 1 4 7 4 8 3 6 4 7
Sample Output
45 56
10 56
3 32
0 0
0 0
0 0
0 0
0 0
0 0
0 0
HINT
第一问直接建出后缀数组之后,算出每一个点有贡献的区间,用height数组从小到大更新答案。
第二问可以用并查集,因为是从小到大做的,所以产生贡献的区间会不断扩大,这样就可以每次把产生贡献的区间
[l,r]
并起来,并且这一段中的元素最多只会属于两个集合,每次在并查集的过程中保存一下当前集合的最大值和最小值更新答案就行了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int N=300010;
char s[N];
LL sum[N],ans[N];
int n,m,t1[N],t2[N],c[N],sa[N],rank[N],height[N],stack[N],l[N],r[N],top,point[N],next[N],tot,en[N],b[N],fa[N],maxn[N],minn[N];
inline int in(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline void add(int x,int y){
next[++tot]=point[x];point[x]=tot;en[tot]=y;
}
inline bool cmp(int *y,int p,int q,int k){
int o0,o1;
o0=p+k>=n?-1:y[p+k];
o1=q+k>=n?-1:y[q+k];
return o0==o1&&y[p]==y[q];
}
inline void build_sa(){
int i,k,p,*x=t1,*y=t2;
for(m=26,i=0;i<m;++i) c[i]=0;
for(i=0;i<n;++i) ++c[x[i]=s[i]-'a'];
for(i=1;i<m;++i) c[i]+=c[i-1];
for(i=n-1;~i;--i) sa[--c[x[i]]]=i;
for(k=1;k<=n;k<<=1){
for(p=0,i=n-k;i<n;++i) y[p++]=i;
for(i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=0;i<m;++i) c[i]=0;
for(i=0;i<n;++i) ++c[x[y[i]]];
for(i=1;i<m;++i) c[i]+=c[i-1];
for(i=n-1;~i;--i) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
x[sa[0]]=0;m=1;
for(i=1;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?m-1:m++;
if(m>=n) break;
}
}
inline void build_height(){
int i,j,k=0;
for(i=0;i<n;++i) rank[sa[i]]=i;
for(i=0;i<n;++i){
if(!rank[i]) continue;
k=k?k-1:k;
j=sa[rank[i]-1];
while(s[i+k]==s[j+k]) ++k;
height[rank[i]]=k;
}
}
inline int find(int x){
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}
int main(){
int i,j;
n=in();
for(i=0;i<n;++i){
char ch=getchar();
while(ch<'a'||ch>'z') ch=getchar();
s[i]=ch;
}
build_sa();
build_height();
for(i=0;i<n;++i){
fa[i]=i;
b[i]=minn[rank[i]]=maxn[rank[i]]=in();
}
stack[top=1]=0;
for(l[0]=0,i=1;i<n;++i){
while(top&&height[stack[top]]>height[i]) --top;
l[i]=top?stack[top]+1:0;
stack[++top]=i;
}
stack[top=1]=n-1;
for(r[n-1]=n-1,i=n-2;i>=0;--i){
while(top&&height[stack[top]]>=height[i]) --top;
r[i]=top?stack[top]-1:n-1;
stack[++top]=i;
}
for(i=0;i<n;++i) add(height[i],i);
memset(ans,128,sizeof(ans));
for(i=n-1;i;--i){
sum[i]=sum[i+1];
ans[i]=ans[i+1];
for(j=point[i];j;j=next[j]){
int o=en[j],L=l[o],R=r[o];
sum[i]+=(LL)(o-L+1)*(LL)(R-o+1);
int r1=find(L-1),r2=find(R);
ans[i]=max(ans[i],max((LL)maxn[r1]*(LL)maxn[r2],(LL)minn[r1]*(LL)minn[r2]));
fa[r2]=r1;
maxn[r1]=max(maxn[r1],maxn[r2]);
minn[r1]=min(minn[r1],minn[r2]);
}
}
sort(b,b+n);
ans[i]=max((LL)b[0]*(LL)b[1],(LL)b[n-1]*(LL)b[n-2]);
sum[0]=(LL)n*(LL)(n-1)/2;
for(i=0;i<n;++i) ans[i]=sum[i]?ans[i]:0;
for(i=0;i<n;++i) printf("%lld %lld\n",sum[i],ans[i]);
}