Description
给出一个长度为 n 的字符串,每一位有一个权值 val。定义两个位字符为 r 相似,是指分别从这两个字符开始,到后面的 r 个字符都相等。两个 r 相似的字符还有一个权值为这两个字符权值的乘积。问对于 r = 0, 1, 2, … , n - 1,统计出有多少种方法可以选出 2 个“r 相似”的字符,并回答选择 2 个”r 相似”的字符可以得到的权值的最大值。
n<=3*10^5
|val| <=10^9
Solution
i和j这两个位置的字符r相似其实就是它们开头的后缀的最长公共前缀大于等于r
那么可以先将height构造出来
对于两个位置i和j,它们排名之间的height的最小值要大于等于r
按照height排序,从大到小做,每扫到一个height那么就利用并查集将它和它的前一名缩起来,并更新相应的r的答案,一开始这个答案还要从之前的继承过来
计算方案数,那么只需要维护并查集时集合的size,合并就相乘计入答案
计算最大值,因为有可能有负数,那么维护集合的最大值和最小值,看乘起来哪个更大
Code
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define imax(a,b) ((a>b)?a:b)
#define N 300005
#define LL long long
using namespace std;
char st[N];
int s2[N],SA[N],rank[N],r1[N],ct[N],n,height[N],d[N],f[N];
LL a[N],ans[N],cnt[N],sz[N],mx[N],mi[N];
void prp()
{
int m=0;
memset(ct,0,sizeof(ct));
fo(i,1,n) ct[rank[i]=st[i]-'a'+1]++,m=imax(m,rank[i]);
fo(i,1,m) ct[i]+=ct[i-1];
fod(i,n,1) SA[ct[rank[i]]--]=i;
for(int j=1,k=0;k<n;j<<=1,m=k)
{
memset(ct,0,sizeof(ct));
int p=0;
fo(i,n-j+1,n) s2[++p]=i;
fo(i,1,n) if(SA[i]>j) s2[++p]=SA[i]-j;
fo(i,1,n) ct[rank[s2[i]]]++;
fo(i,1,m) ct[i]+=ct[i-1];
fod(i,n,1) SA[ct[rank[s2[i]]]--]=s2[i];
r1[SA[1]]=k=1;
fo(i,2,n) r1[SA[i]]=(rank[SA[i]]==rank[SA[i-1]]&&rank[SA[i]+j]==rank[SA[i-1]+j])?k:++k;
fo(i,1,n) rank[i]=r1[i];
}
}
void geth()
{
height[1]=0;
fo(i,1,n)
{
if(rank[i]==1) continue;
int p=imax(0,height[rank[i-1]]-1);
while(st[i+p]==st[SA[rank[i]-1]+p]) p++;
height[rank[i]]=p;
}
}
bool cmp(int x,int y)
{
return height[x]>height[y];
}
int getf(int k)
{
if(f[k]==0||f[k]==k) return k;
return f[k]=getf(f[k]);
}
int main()
{
cin>>n;
scanf("\n%s",st+1);
fo(i,1,n) scanf("%lld",&a[i]);
prp();
geth();
fo(i,1,n) d[i]=i,sz[i]=1,mx[i]=mi[i]=a[SA[i]];
sort(d+1,d+1+n,cmp);
fo(i,0,n) ans[i]=-7000000000000000000;
fo(i,1,n)
{
int w=height[d[i]];
if(w!=height[d[i-1]]) cnt[w]+=cnt[height[d[i-1]]],ans[w]=max(ans[w],ans[height[d[i-1]]]);
if(d[i]==1) continue;
int fx=getf(d[i]),fy=getf(d[i]-1);
cnt[w]+=sz[fx]*sz[fy];
ans[w]=max(ans[w],max(mx[fx]*mx[fy],mi[fx]*mi[fy]));
f[fx]=fy,sz[fy]+=sz[fx],mx[fy]=max(mx[fy],mx[fx]),mi[fy]=min(mi[fy],mi[fx]);
}
fod(i,n-1,0)
{
ans[i]=max(ans[i],ans[i+1]);
if(cnt[i]==0) cnt[i]=cnt[i+1];
}
fo(i,0,n-1)
{
if(ans[i]==-7000000000000000000) ans[i]=0;
printf("%lld %lld\n",cnt[i],ans[i]);
}
}