博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)
阅读量:4615 次
发布时间:2019-06-09

本文共 3667 字,大约阅读时间需要 12 分钟。

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

题面

题解

贪心的想法是从左往右,能选就选。这个显然是正确的。

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

#include
#include
#include
using namespace std;#define ll long long#define MAX 200100inline 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<
<=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
=len)++r; for(int j=l;j<=r;++j)if(SA.SA[j]<=n)pos[++cnt]=SA.SA[j]; if(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]
>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<
0&&SA.hg[i][l-(1<
=len)l-=1<
=len)r+=1<
t||!v)break; ans[Q[pos].id]+=K-v; } } for(int i=1;i<=m;++i)printf("%lld\n",ans[i]); return 0;}

转载于:https://www.cnblogs.com/cjyyb/p/10407010.html

你可能感兴趣的文章
linux 命令大全
查看>>
MongoDB数据库
查看>>
移动端经常出现的兼容问题,谈谈移动端应用或者wap站的一些优化技巧和心得...
查看>>
央行930新政启动房贷证券化 将撬动10万亿进楼市
查看>>
MySql 日期比较大小
查看>>
软件错误
查看>>
gdb
查看>>
tmux使用总结
查看>>
[LeetCode#133]Clone Graph
查看>>
[luogu3980] 志愿者招募
查看>>
转账-AOP基于xml和基于注解
查看>>
Path Sum II
查看>>
ASP.NET SignalR 系列(一)之SignalR介绍
查看>>
Java---字节输入,文件操作,病毒制造,请谨慎运行!
查看>>
Css3在移动设备上的优化点
查看>>
poj 2029 Get Many Persimmon Trees 矩形内部点统计,递推动态规划
查看>>
HDU 2089 不要 62
查看>>
排序算法之——桶排序
查看>>
Linux压力测试工具
查看>>
根据节点的绝对路径创建Xml
查看>>