2021HDU多校第四场 HDU6975 Display Substring
题意
给出26个小写字母的权值(c_alpha),定义一个串(S)的权值为其所有字符的权值和。现给出一个仅由小写字符组成的串(S),求其所有本质不同的子串中权值第(k)小的串。
(1le nle 10^5,1le kle frac{n(n+1)}{2},sum|S|le8 imes10^5,1le c_alphale 100)
题解
二分答案,考虑如何统计(S)中是否有(k)个比权值当前答案小的不同字串。首先对(S)建(SAM),由于字符的权值都是正的,显然串越长,其权值越大,又由于(parent)树上的每个节点代表(S)中某个前缀的一段连续后缀,我们可以直接对(parent)树上的每个节点分别二分将结果加起来即可。复杂度(O(nlog^2n))。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define I inline
using namespace std;
typedef long long ll;
const int N=800005,M=26;
char S[N];
int n,m,x,y,c[M];
ll ss[N],K;
ll getlr(int x,int y){return ss[y]-ss[x-1];}
struct sam{
int fa[N],sz[N],len[N],lst,gt,ch[N][M],pos[N];
void init(){gt=lst=1;}
void init2(){
for(int p=1;p<=gt;p++){
pos[p]=0;
memset(ch[p],0,sizeof(ch[p]));
}
lst=gt=1;
}
void ins(int c,int id){
int f=lst,p=++gt;lst=p;
len[p]=len[f]+1;sz[p]=1;pos[p]=id;
while(f&&!ch[f][c])ch[f][c]=p,f=fa[f];
if(!f){fa[p]=1;return ;}
int x=ch[f][c],y=++gt;
if(len[x]==len[f]+1){gt--;fa[p]=x;return ;}
len[y]=len[f]+1;pos[y]=pos[x];fa[y]=fa[x];fa[x]=fa[p]=y;
for(int i=0;i<M;i++)ch[y][i]=ch[x][i];
while(f&&ch[f][c]==x)ch[f][c]=y,f=fa[f];
}
int A[N],c[N];
void rsort(){
for(int i=1;i<=gt;i++){c[i]=0;}
for(int i=1;i<=gt;i++)++c[len[i]];
for(int i=1;i<=gt;i++)c[i]+=c[i-1];
for(int i=gt;i>=1;i--){A[c[len[i]]--]=i;}
}
void f1(){
rsort();
}
int ck(ll k){
ll res=0;
for(int i=2;i<=gt;i++){
int l=1,r=len[i]-len[fa[i]],mid=0;
int ans=0;
while(l<=r){
mid=l+r>>1;
if(getlr(pos[i]-mid+1-len[fa[i]],pos[i])<=k){
ans=mid;
l=mid+1;
}
else{r=mid-1;}
}
res+=ans;
}
return res>=K;
}
}g;
void f1(){
scanf("%d%lld%s",&n,&K,S+1);
g.init();
for(int i=1;i<=n;i++){
g.ins(S[i]-'a',i);
//upd(rt[g.lst],1,n,i);
}
for(int i=0;i<M;i++){scanf("%d",&c[i]);}
for(int i=1;i<=n;i++){ss[i]=ss[i-1]+c[S[i]-'a'];}
ll ans=0,l=0,r=ss[n],mid=0;
while(l<=r){
mid=l+r>>1;
if(g.ck(mid)){ans=mid;r=mid-1;}
else{l=mid+1;}
}
if(ans<=0||ans>ss[n]){printf("-1
");g.init2();return ;}
printf("%lld
",ans);
g.init2();
}
int main(){
int t;scanf("%d",&t);
while(t--)
f1();
return 0;
}