zoukankan      html  css  js  c++  java
  • 【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)

    【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)

    题面

    BZOJ
    洛谷

    题解

    贪心的想法是从左往右,能选就选。这个显然是正确的。
    题目的数据范围很好的说明了要对于询问分开进行处理。
    先考虑询问的模板串长比较大的情况。
    那么只需要每次找到一个范围内的最小位置然后接着暴力跳就可以了。
    这个这个过程可以把(AB)两个串拼接在一起求(SA),这样能够匹配上(P)串的(A)的后缀的起始位置在(SA)上就是一段连续区间。考虑每次找出在(A)([l,r])范围内的合法的最小值,那么对于后缀数组的每个位置对应的(A)上的位置构建主席树,这样子每次就可以在主席树上进行二分来寻找到第一个合法的位置。这样一来反复横跳就可以得到匹配的答案。
    这样子的时间复杂度是(O(n/|P|*logn)),当给定串长比较大的时候是可以接受的。
    但是当(|P|)很小的时候显然不能暴跳,因此肯定需要预处理答案。
    对于每个(len)进行预处理,显然每个合法的串的起始位置都可以找到唯一的一个最小的合法位置(或者不存在),满足以这两个位置为起始位置往后匹配(len)位相等,并且两个长度为(len)的串不交。
    显然这样子的结构构成了一棵树,那么把树给构出来之后,对于小于特定值的询问在树上倍增计算即可。
    根据题目数据范围显然两种方法的分界线是(50)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define MAX 200100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,K,m,lg[MAX];
    char A[MAX];
    struct SuffixArray
    {
    	int hg[20][MAX],SA[MAX],rk[MAX];
    	int a[MAX],t[MAX],x[MAX],y[MAX];
    	bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
    	void GetSA()
    	{
    		int m=255;
    		for(int i=1;i<=n;++i)t[x[i]=a[i]]+=1;
    		for(int i=1;i<=m;++i)t[i]+=t[i-1];
    		for(int i=n;i>=1;--i)SA[t[x[i]]--]=i;
    		for(int k=1;k<=n;k<<=1)
    		{
    			int p=0;
    			for(int i=n-k+1;i<=n;++i)y[++p]=i;
    			for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;
    			for(int i=1;i<=m;++i)t[i]=0;
    			for(int i=1;i<=n;++i)t[x[y[i]]]+=1;
    			for(int i=1;i<=m;++i)t[i]+=t[i-1];
    			for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];
    			swap(x,y);x[SA[1]]=p=1;
    			for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
    			if(p>=n)break;m=p;
    		}
    		for(int i=1;i<=n;++i)rk[SA[i]]=i;
    		for(int i=1,j=0;i<=n;++i)
    		{
    			if(j)--j;
    			while(a[i+j]==a[SA[rk[i]+1]+j])++j;
    			hg[0][rk[i]]=j;
    		}
    		for(int j=1;j<=lg[n];++j)
    			for(int i=1;i+(1<<j)-1<=n;++i)
    				hg[j][i]=min(hg[j-1][i],hg[j-1][i+(1<<(j-1))]);
    	}
    }SA;
    struct SegmentTree
    {
    	struct Node{int ls,rs,v;}t[MAX*20];
    	int tot;
    	void Modify(int &x,int l,int r,int p)
    	{
    		t[++tot]=t[x];x=tot;t[x].v+=1;
    		if(l==r)return;int mid=(l+r)>>1;
    		if(p<=mid)Modify(t[x].ls,l,mid,p);
    		else Modify(t[x].rs,mid+1,r,p);
    	}
    	int Query(int x,int y,int l,int r,int p)
    	{
    		if(!(t[y].v-t[x].v))return 0;
    		if(l==r)return l;
    		int mid=(l+r)>>1,ret=0;
    		if(p<=mid)ret=Query(t[x].ls,t[y].ls,l,mid,p);
    		if(!ret)ret=Query(t[x].rs,t[y].rs,mid+1,r,p);
    		return ret;
    	}
    }T;
    int rt[MAX];ll ans[MAX];
    struct Qry{int s,t,l,len,pos,id;}Q[MAX];
    bool operator<(Qry a,Qry b){return a.len!=b.len?a.len<b.len:a.pos<b.pos;}
    int fa[20][MAX];ll sum[20][MAX];
    void Work(int len,int &L)
    {
    	static int pos[MAX];
    	for(int l=1,r=l;l<=n+n+1;r=l=r+1)
    	{
    		if(Q[L].len!=len)return;
    		int cnt=0;
    		while(SA.hg[0][r]>=len)++r;
    		for(int j=l;j<=r;++j)if(SA.SA[j]<=n)pos[++cnt]=SA.SA[j];
    		if(Q[L].pos<l||Q[L].pos>r)continue;
    		sort(&pos[1],&pos[cnt+1]);pos[cnt+1]=n+n+1;
    		int k=1;
    		for(int i=1;i<=cnt;++i)
    		{
    			while(pos[k]-pos[i]<len)++k;
    			fa[0][i]=k;sum[0][i]=K-pos[i];
    		}
    		int lim=lg[min(cnt+1,n/len)];
    		for(int j=1;j<=lim;++j)
    			for(int i=1;i<=cnt;++i)
    				fa[j][i]=fa[j-1][fa[j-1][i]],sum[j][i]=sum[j-1][i]+sum[j-1][fa[j-1][i]];
    		while(Q[L].len==len&&l<=Q[L].pos&&Q[L].pos<=r)
    		{
    			int s=Q[L].s,t=Q[L].t,x=lower_bound(&pos[1],&pos[cnt+2],s)-pos;
    			if(pos[x]<=t)
    			{
    				int u=x;ll ret=0;
    				for(int i=lim;~i;--i)
    					if(pos[fa[i][u]]&&pos[fa[i][u]]<=t)
    						ret+=sum[i][u],u=fa[i][u];
    				ans[Q[L].id]=ret+sum[0][u];
    			}
    			++L;
    		}
    		for(int i=0;i<=lim;++i)
    			for(int j=1;j<=cnt+1;++j)
    				fa[i][j]=sum[i][j]=0;
    	}
    }
    int main()
    {
    	n=read();K=read();
    	scanf("%s",A+1);A[n+1]='#';scanf("%s",A+n+2);
    	n=n+n+1;for(int i=1;i<=n;++i)SA.a[i]=A[i];
    	for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
    	SA.GetSA();n>>=1;
    	m=read();
    	for(int i=1;i<=m;++i)
    	{
    		int s=read(),t=read(),l=read(),r=read();
    		Q[i]=(Qry){s,t-(r-l),l,r-l+1,SA.rk[n+1+l],i};
    	}
    	sort(&Q[1],&Q[m+1]);
    	int pos=1;
    	for(int i=1;i<=n&&i<=50;++i)Work(i,pos);
    	for(int i=1;i<=n+n+1;++i)
    		if(SA.SA[i]<=n)T.Modify(rt[i]=rt[i-1],1,n,SA.SA[i]);
    		else rt[i]=rt[i-1];
    	for(;pos<=m;++pos)
    	{
    		int s=Q[pos].s,t=Q[pos].t,len=Q[pos].len;
    		int x=Q[pos].pos,l=x,r=x;
    		for(int i=lg[n]+1;~i;--i)
    			if(l-(1<<i)>0&&SA.hg[i][l-(1<<i)]>=len)l-=1<<i;
    		for(int i=lg[n]+1;~i;--i)
    			if(r+(1<<i)<=n+n+1&&SA.hg[i][r]>=len)r+=1<<i;
    		for(int u=s,v;u<=n;u=v+len)
    		{
    			v=T.Query(rt[l-1],rt[r],1,n,u);
    			if(v>t||!v)break;
    			ans[Q[pos].id]+=K-v;
    		}
    	}
    	for(int i=1;i<=m;++i)printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    解释下Http请求头和常见响应状态码
    sys 模块常用方法
    os 模块常用方法
    说明os,sys模块有什么不同
    dict 的 items() 方法与 iteritems() 方法的不同?
    Python是如何进行类型转换的?
    Python中pass语句的作用是什么?
    创建一个简单tcp服务器需要的流程
    安全性
    传输数据的大小
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10407010.html
Copyright © 2011-2022 走看看