其他分享
首页 > 其他分享> > 【题解】2021HDU多校第四场 HDU6975 Display Substring

【题解】2021HDU多校第四场 HDU6975 Display Substring

作者:互联网

2021HDU多校第四场 HDU6975 Display Substring

题意

给出26个小写字母的权值\(c_\alpha\),定义一个串\(S\)的权值为其所有字符的权值和。现给出一个仅由小写字符组成的串\(S\),求其所有本质不同的子串中权值第\(k\)小的串。

\(1\le n\le 10^5,1\le k\le \frac{n(n+1)}{2},\sum|S|\le8\times10^5,1\le c_\alpha\le 100\)

题解

二分答案,考虑如何统计\(S\)中是否有\(k\)个比权值当前答案小的不同字串。首先用对\(S\)建\(SAM\),由于字符的权值都是正的,显然串越长,其权值越大,又由于\(parent\)树上的每个节点代表\(S\)中某个前缀的一段连续后缀,我们可以直接对\(parent\)树上的每个节点分别二分将结果加起来即可。复杂度\(O(n\log^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\n");g.init2();return ;}
	printf("%lld\n",ans);
	g.init2();
}
int main(){
	int t;scanf("%d",&t);
	while(t--)
		f1();
	return 0;
}

标签:le,第四场,int,题解,ll,2021HDU,mid,权值,void
来源: https://www.cnblogs.com/bobh/p/15092782.html