首先是正解部分…
我们先用后缀数组将后缀排序并得到hei数组…
然后正解的做法是用合并后缀集合的方法维护信息(太神了自己想根本想不到OLZ…
初始每个后缀自己构成一个大小为1的集合。按hei[i]从大到小枚举每个后缀,将rank为i的后缀所在集合与rank为i-1的后缀所在集合合并(此时对于前一集合中任意后缀T1与后一集合中任意后缀T2,都有LCP(T1,T2)=hei[i]),具体怎么合并和维护信息看代码.
AC code:
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N=300010;
const ll INF=1<<30;
int n;
int a[N],b[N],f[N],sa[N],ta[N],c[N],cc[N],rk[N],tr[N],hei[N],mx[N],mn[N],sz[N];
ll A1[N],A2[N];
char s[N];
struct Data{
int val,num;
Data() {}
Data(int val,int num):val(val),num(num) {}
friend bool operator<(Data x,Data y){
if(x.val!=y.val) return x.val>y.val;
return x.num>y.num;
}
};
vector<Data> v;
int cmp(int i,int j,int l){
return i+l>=n||j+l>=n||rk[i]!=rk[j]||rk[i+l]!=rk[j+l];
}
void build(){
for(int i=0;i<n;i++) c[s[i]]=1,cc[s[i]]++;
for(int i=1;i<(1<<10);i++) c[i]+=c[i-1],cc[i]+=cc[i-1];
for(int i=0;i<n;i++) rk[i]=c[s[i]];
for(int i=0;i<n;i++) sa[cc[s[i]]--]=i;
for(int i=1,j,k;rk[sa[n]]!=n;i<<=1){
for(j=1;j<=i;j++) ta[j]=n-j;
for(k=1;k<=n;k++) if(sa[k]>=i) ta[j++]=sa[k]-i;
for(j=0;j<n;j++) c[j]=0;
for(j=0;j<n;j++) c[rk[j]]++;
for(j=1;j<n;j++) c[j]+=c[j-1];
for(j=n;j>0;j--) sa[c[rk[ta[j]]]--]=ta[j];
for(tr[sa[1]]=1,j=2;j<=n;j++) tr[sa[j]]=tr[sa[j-1]]+cmp(sa[j],sa[j-1],i);
for(j=0;j<n;j++) rk[j]=tr[j];
}
for(int i=0,j=0;i<n;hei[rk[i]]=j,j=j?j-1:0,i++){
for(;rk[i]!=1&&s[sa[rk[i]]+j]==s[sa[rk[i]-1]+j];j++) ;
}
}
int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
void work(){
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=n;i++) sz[i]=1;
for(int i=1;i<=n;i++) b[rk[i-1]]=a[i];
for(int i=1;i<=n;i++) mx[i]=mn[i]=b[i];
for(int i=0;i<n;i++) A2[i]=-(INF*INF);
for(int i=1;i<=n;i++) v.push_back(Data(hei[i],i));
sort(v.begin(),v.end());
for(int i=0;i<n-1;i++){
int x=find(v[i].num-1),y=find(v[i].num);
ll a=mx[x],b=mn[x],c=mx[y],d=mn[y];
A1[v[i].val]+=(ll)sz[x]*(ll)sz[y];
A2[v[i].val]=max(A2[v[i].val],max(a*c,max(b*d,max(a*d,b*c))));
f[x]=y;
sz[y]+=sz[x];
mx[y]=max(mx[y],mx[x]);mn[y]=min(mn[y],mn[x]);
}
}
int main(){
scanf("%d%s",&n,s);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build();
work();
for(int i=n-2;i>=0;i--){
A1[i]+=A1[i+1];
A2[i]=max(A2[i],A2[i+1]);
}
for(int i=0;i<n;i++) printf("%lld %lld\n",A1[i],A1[i]?A2[i]:0);
return 0;
}
接下来是吐槽部分…
这题用BZOJ3238的方法会
T !!!!!!!!!!!!!!!!!!!!
各种优化常数之后还是过不了(最大原因是倍增RMQ慢了,正解并没有用到倍增RMQ)!!!!!!!!!!!!!!!!!!!!!!最后还是T了两个点,真是跪了…
不过在优化的过程中还是有一些收获的,写一下…
- 区间最值那里分类讨论写复杂了(再次证明了我有多弱),之后一定要想清楚再写代码.
- 注意用long long会慢一倍左右,所以代码中全部变量都用long long是作死的行为…
- 原先的后缀数组模板还有可以改进的地方,可以少写几个for,具体看代码.
贴一下90分代码吧…
TLE code:
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=300010;
const int LOGN=25;
const ll INF=1<<30;
int n;
int a[N],b[N],lg2[N],sa[N],ta[N],c[N],cc[N],rk[N],tr[N],hei[N];
int f[N][LOGN],g1[N][LOGN],g2[N][LOGN];
ll A1[N],A2[N];
char s[N];
int cmp(int i,int j,int l){
return i+l>=n||j+l>=n||rk[i]!=rk[j]||rk[i+l]!=rk[j+l];
}
void build(){
for(int i=0;i<n;i++) c[s[i]]=1,cc[s[i]]++;
for(int i=1;i<(1<<10);i++) c[i]+=c[i-1],cc[i]+=cc[i-1];
for(int i=0;i<n;i++) rk[i]=c[s[i]];
for(int i=0;i<n;i++) sa[cc[s[i]]--]=i;
for(int i=1,j,k;rk[sa[n]]!=n;i<<=1){
for(j=1;j<=i;j++) ta[j]=n-j;
for(k=1;k<=n;k++) if(sa[k]>=i) ta[j++]=sa[k]-i;
for(j=0;j<n;j++) c[j]=0;
for(j=0;j<n;j++) c[rk[j]]++;
for(j=1;j<n;j++) c[j]+=c[j-1];
for(j=n;j>0;j--) sa[c[rk[ta[j]]]--]=ta[j];
for(tr[sa[1]]=1,j=2;j<=n;j++) tr[sa[j]]=tr[sa[j-1]]+cmp(sa[j],sa[j-1],i);
for(j=0;j<n;j++) rk[j]=tr[j];
}
for(int i=0,j=0;i<n;hei[rk[i]]=j,j=j?j-1:0,i++){
for(;rk[i]!=1&&s[sa[rk[i]]+j]==s[sa[rk[i]-1]+j];j++) ;
}
for(int i=1;i<=n;i++) b[i]=a[sa[i]+1];
for(int i=1;i<=n;i++) a[i]=b[i];
for(int i=1;i<=n;i++){
f[i][0]=hei[i];
g1[i][0]=g2[i][0]=a[i];
}
for(int i=1,j;i<=lg2[n];i++){
for(j=1;j<=n;j++){
int tmp=j+(1<<(i-1))>n?0:(1<<(i-1));
f[j][i]=f[j][i-1]<f[j+tmp][i-1]?f[j][i-1]:f[j+tmp][i-1];
g1[j][i]=g1[j][i-1]>g1[j+tmp][i-1]?g1[j][i-1]:g1[j+tmp][i-1];
g2[j][i]=g2[j][i-1]<g2[j+tmp][i-1]?g2[j][i-1]:g2[j+tmp][i-1];
}
}
}
int Max(int g[N][LOGN],int L,int R){
return max(g[L][lg2[R-L+1]],g[R-(1<<(lg2[R-L+1]))+1][lg2[R-L+1]]);
}
int Min(int g[N][LOGN],int L,int R){
return min(g[L][lg2[R-L+1]],g[R-(1<<(lg2[R-L+1]))+1][lg2[R-L+1]]);
}
int main(){
scanf("%d%s",&n,s);
for(int i=1;i<=n;i++) lg2[i]=(int)log2(i);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build();
for(int i=0;i<n;i++) A2[i]=-(INF*INF);
for(int i=2;i<=n;i++){
int L1=1,R1=i,L2=i,R2=n+1;
while(L1+1!=R1){
int M=(L1+R1)>>1;
if(Min(f,M,i-1)>hei[i]) R1=M;
else L1=M;
}
while(L2+1!=R2){
int M=(L2+R2)>>1;
if(Min(f,i,M)<hei[i]) R2=M;
else L2=M;
}
A1[hei[i]]+=(ll)(i-R1+1)*(ll)(L2-i+1);
ll a=Max(g1,R1-1,i-1),b=Max(g1,i,L2),c=Min(g2,R1-1,i-1),d=Min(g2,i,L2);
A2[hei[i]]=max(A2[hei[i]],max(a*b,max(c*d,max(a*d,b*c))));
}
for(int i=n-2;i>=0;i--){
A1[i]+=A1[i+1];
A2[i]=max(A2[i],A2[i+1]);
}
for(int i=0;i<n;i++) printf("%lld %lld\n",A1[i],A1[i]?A2[i]:0);
return 0;
}