P2178 [NOI2015] 品酒大会
P2178 [NOI2015] 品酒大会
题意:
因为
求权值乘积的最大值可以用
因为
建议认真思考区间边界。
#include<bits/stdc++.h>
#define N 300010
#define ll long long
using namespace std;
inline int read(){
int w=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=0;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return f?w:-w;
}
char s[N];
ll res[N],sum[N],f[2][N<<1][25];
int a[N],n,m,sa[N],rk[N],se[N],cnt[N],ht[N],logg[N],stac[N],top,p[N];
inline void qsort(){
for(int i=0;i<=m;i++) cnt[i]=0;
for(int i=1;i<=n;i++) cnt[rk[i]]++;
for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--) sa[cnt[rk[se[i]]]--]=se[i];
}
inline void get_sa(){
m=122;
for(int i=1;i<=n;i++) rk[i]=s[i],se[i]=i;
qsort();
for(int w=1;w<=n;w<<=1){
int num=0;
for(int i=n-w+1;i<=n;i++) se[++num]=i;
for(int i=1;i<=n;i++) if(sa[i]>w) se[++num]=sa[i]-w;
qsort();swap(rk,se);rk[sa[1]]=num=1;
for(int i=2;i<=n;i++) rk[sa[i]]=(se[sa[i]]==se[sa[i-1]]&&se[sa[i]+w]==se[sa[i-1]+w])?num:++num;
if(num==n)break;m=num;
}
for(int i=1,j=0;i<=n;i++){
if(j)j--;
while(s[sa[rk[i]]+j]==s[sa[rk[i]-1]+j])j++;
ht[rk[i]]=j;
}
}
inline ll askmi(int l,int r){
int k=logg[r-l+1];
return min(f[0][l][k],f[0][r-(1<<k)+1][k]);
}
inline ll askma(int l,int r){
int k=logg[r-l+1];
return max(f[1][l][k],f[1][r-(1<<k)+1][k]);
}
int main(){
n=read();scanf("%s",s+1);
for(int i=1;i<=n;i++) a[i]=read(),res[i]=-2e18;
//求ht数组
get_sa();
//ST表
for(int i=1;i<=n;i++) f[0][i][0]=f[1][i][0]=a[sa[i]];
for(int i=2;i<N;i++) logg[i]=logg[i>>1]+1;
for(int j=1;j<=logg[n];j++)
for(int i=1;i+(1<<(j-1))-1<=n;i++)
f[0][i][j]=min(f[0][i][j-1],f[0][i+(1<<(j-1))][j-1]),
f[1][i][j]=max(f[1][i][j-1],f[1][i+(1<<(j-1))][j-1]);
//单调栈求贡献区间
stac[top]=1;//注意不要算ht[1]的贡献
for(int i=2;i<=n;i++){
while(top&&ht[stac[top]]>ht[i]){
int x=stac[top],l=p[x],r=i-1;
sum[ht[x]]+=1ll*(x-l+1)*(r-x+1);//个数
if(l<=r) res[ht[x]]=max(res[ht[x]],max(askmi(l-1,x-1)*askmi(x,r),askma(l-1,x-1)*askma(x,r)));//权值最大值
top--;
}
p[i]=stac[top]+1;//左端点
stac[++top]=i;
}
//将栈中元素清空
while(top){
int x=stac[top],l=p[x],r=n;//后面没有比它小的,右端点就只能为n
sum[ht[x]]+=1ll*(x-l+1)*(r-x+1);
if(l<=r) res[ht[x]]=max(res[ht[x]],max(askmi(l-1,x-1)*askmi(x,r),askma(l-1,x-1)*askma(x,r)));
top--;
}
for(int i=n-1;i>=0;i--) sum[i]+=sum[i+1],res[i]=max(res[i],res[i+1]);//求个后缀和/max
for(int i=0;i<n;i++) printf("%lld %lld\n",sum[i],res[i]==-2e18?0:res[i]);
return 0;
}