bzoj上居然没题面。。可怕。洛谷上都有。首先,r相似其实就是lcp为r。那么就有个显然的做法。每次枚举lcp,按h分组,一组内有cnt个,则答案就是在cnt个数中选两个,C(cnt 2)。符号不会打,意思到了就好。。然后最大值。。想想怎么维护就好了。直接看代码吧。然后就可以拿到50分。
而正解是这样的:r相似是不是至少有r+1相似个?那么r+1相似有多少个是不是跟r相似没啥关系??所以我们可以把所有的h从大到小排序,按顺序做。ans[i]=ans[i+1].而这一次答案可能会更多,来源于h[p]==i的两个串。我们用并查集维护一下。每次多的其实就是两个块的两两匹配,即size[x]*size[y].做完我们就把这两个串合起来。。至于最大值怎么更新。见代码吧。
其实还可以这样理解下:我们以前做的按h分组,是不是h越大,分组越散?h逐渐变小,一个个分组就连起来了??像不像并查集!!!
50分做法:
#include <cstdio>
#include <cstring>
#define ll long long
#define N 300010
#define inf 1000000000000000001;
inline int read(){
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 ll max(ll x,ll y){return x>y?x:y;}
int n,m,a[N],rank[N<<1],rank1[N],count[N],sa[N],h[N],tmp[N];
char s[N];
inline bool solve(int len){
ll cnt=0,ans=0,mx=-inf;
int a1=0,a2=0,b1=0,b2=0;//a>0,b<0
for(int i=1;i<=n+1;++i){
if(h[i]>=len){
++cnt;if(a[sa[i]]>a1) a2=a1,a1=a[sa[i]];
else if(a[sa[i]]>a2) a2=a[sa[i]];
else if(a[sa[i]]<b1) b2=b1,b1=a[sa[i]];
else if(a[sa[i]]<b2) b2=a[sa[i]];
}
else{
if(cnt>=2){
ans+=(ll)cnt*(cnt-1)/2;
if(a2==0&&b2==0) mx=max(mx,(ll)a1*b1);
else if(a2==0) mx=max(mx,(ll)b1*b2);
else if(b2==0) mx=max(mx,(ll)a1*a2);
else mx=max(mx,max((ll)a1*a2,(ll)b1*b2));
}
cnt=1;a1=a2=b1=b2=0;
if(a[sa[i]]>0) a1=a[sa[i]];else b1=a[sa[i]];
}
}
if(ans==0){printf("0 0\n");return 0;}
else{
printf("%lld %lld\n",ans,mx);return 1;
}
}
int main(){
freopen("a.in","r",stdin);
n=read();scanf("%s",s+1);for(int i=1;i<=n;++i) a[i]=read();
m='z'+1;
for(int i=1;i<=m;++i) count[i]=0;
for(int i=1;i<=n;++i) count[s[i]]=1;
for(int i=1;i<=m;++i) count[i]+=count[i-1];
for(int i=n;i>=1;--i) rank[i]=count[s[i]];
int k=0;m=27;
for(int p=1;k!=n;p<<=1,m=k){
for(int i=1;i<=m;++i) count[i]=0;
for(int i=1;i<=n;++i) count[rank[i+p]]++;
for(int i=1;i<=m;++i) count[i]+=count[i-1];
for(int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
for(int i=1;i<=m;++i) count[i]=0;
for(int i=1;i<=n;++i) count[rank[tmp[i]]]++;
for(int i=1;i<=m;++i) count[i]+=count[i-1];
for(int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
memcpy(rank1,rank,sizeof(rank1));
rank[sa[1]]=k=1;
for(int i=2;i<=n;++i){
if(rank1[sa[i]]!=rank1[sa[i-1]]||rank1[sa[i]+p]!=rank1[sa[i-1]+p]) ++k;
rank[sa[i]]=k;
}
}k=0;
for(int i=1;i<=n;++i){
if(rank[i]==1){h[1]=0;continue;}
if(i==1||h[rank[i-1]]<=1) k=0;
if(k) --k;
while(s[i+k]==s[sa[rank[i]-1]+k]) ++k;
h[rank[i]]=k;
}h[n+1]=-1;
for(int i=0;i<n;++i){
if(!solve(i)){
for(int j=i+1;j<n;++j) printf("0 0\n");break;
}
}
return 0;
}
正解:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define N 300010
inline int read(){
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;
}
template <typename T>
inline T max(T x,T y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
struct node{
int h,x,y;
}data[N];
int n,m,a[N],rank[N<<1],rank1[N],count[N],sa[N],h[N],tmp[N];
int fa[N],size[N],mx[N],mn[N];
ll ans[N],ansmx[N];
char s[N];
inline bool cmp(node x,node y){return x.h>y.h;}
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){
fa[y]=x;size[x]+=size[y];
mx[x]=max(mx[x],mx[y]);
mn[x]=min(mn[x],mn[y]);
}
int main(){
// freopen("a.in","r",stdin);
n=read();scanf("%s",s+1);for(int i=1;i<=n;++i) a[i]=read();
m='z'+1;
for(int i=1;i<=m;++i) count[i]=0;
for(int i=1;i<=n;++i) count[s[i]]=1;
for(int i=1;i<=m;++i) count[i]+=count[i-1];
for(int i=n;i>=1;--i) rank[i]=count[s[i]];
int k=0;m=27;
for(int p=1;k!=n;p<<=1,m=k){
for(int i=1;i<=m;++i) count[i]=0;
for(int i=1;i<=n;++i) count[rank[i+p]]++;
for(int i=1;i<=m;++i) count[i]+=count[i-1];
for(int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
for(int i=1;i<=m;++i) count[i]=0;
for(int i=1;i<=n;++i) count[rank[tmp[i]]]++;
for(int i=1;i<=m;++i) count[i]+=count[i-1];
for(int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
memcpy(rank1,rank,sizeof(rank1));
rank[sa[1]]=k=1;
for(int i=2;i<=n;++i){
if(rank1[sa[i]]!=rank1[sa[i-1]]||rank1[sa[i]+p]!=rank1[sa[i-1]+p]) ++k;
rank[sa[i]]=k;
}
}k=0;
for(int i=1;i<=n;++i){
if(rank[i]==1){h[1]=0;continue;}
if(i==1||h[rank[i-1]]<=1) k=0;
if(k) --k;
while(s[i+k]==s[sa[rank[i]-1]+k]) ++k;
h[rank[i]]=k;
}
memset(ansmx,128,sizeof(ansmx));
for(int i=1;i<=n;++i) fa[i]=i,size[i]=1,mx[i]=mn[i]=a[sa[i]];
for(int i=1;i<n;++i) data[i].h=h[i+1],data[i].x=i,data[i].y=i+1;
std::sort(data+1,data+n,cmp);
for(int i=data[1].h,p=1;i>=0;--i){
ans[i]=ans[i+1];ansmx[i]=ansmx[i+1];
while(p<=n-1&&data[p].h==i){
int x=find(data[p].x),y=find(data[p].y);
ans[i]+=(ll)size[x]*size[y];
ansmx[i]=max(ansmx[i],(ll)mx[x]*mx[y]);
ansmx[i]=max(ansmx[i],(ll)mn[x]*mn[y]);
merge(x,y);++p;
}
}
for(int i=0;i<n;++i){
printf("%lld %lld\n",ans[i],ans[i]==0?0:ansmx[i]);
}
return 0;
}