【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\n",ans[i]);
return 0;
}

【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)的更多相关文章

  1. LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增

    题面: https://loj.ac/problem/2720 考虑枚举T串的每个后缀i,我们要做两件事. 一.统计有多少子串[i,j]在S中要求位置出现. 二.去重. 第二步好做,相当于在后缀数组上 ...

  2. BZOJ5304 : [Haoi2018]字串覆盖

    离线处理所有询问. 对于$r-l\leq 50$的情况: 按照串长从$1$到$51$分别把所有子串按照第一位字符为第一关键字,上一次排序结果为第二关键字进行$O(n)$基数排序. 同理也可以用上一次比 ...

  3. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  4. 洛谷P2408 不同字串个数 [后缀数组]

    题目传送门 不同字串个数 题目背景 因为NOI被虐傻了,蒟蒻的YJQ准备来学习一下字符串,于是它碰到了这样一道题: 题目描述 给你一个长为N的字符串,求不同的子串的个数 我们定义两个子串不同,当且仅当 ...

  5. [BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MB Description 佳媛姐姐过生日的时候,她的小 ...

  6. [2019CCPC网络赛][hdu6704]K-th occurrence(后缀数组&&主席树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6704 题意为查询子串s[l...r]第k次出现的位置. 写完博客后5分钟的更新 写完博客才发现这份代码 ...

  7. HDU-6704 K-th occurrence(后缀数组+主席树)

    题意 给一个长度为n的字符串,Q次询问,每次询问\((l,r,k)\) , 回答子串\(s_ls_{l+1}\cdots s_r\) 第\(k\) 次出现的位置,若不存在输出-1.\(n\le 1e5 ...

  8. BZOJ3473:字符串(后缀数组,主席树,二分,ST表)

    Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 一 ...

  9. P5346 【XR-1】柯南家族(后缀数组+主席树)

    题目 P5346 [XR-1]柯南家族 做法 聪明性是具有传递性的,且排列是固定的 那么先预处理出每个点的名次,用主席树维护\(k\)大值 一眼平衡树,遍历的同时插入\(O(log^2n)\),总时间 ...

随机推荐

  1. PlainElastic.Net

    PlainElastic.Net PlainElastic.Net The really plain Elastic Search .Net client. Idea Installation How ...

  2. react render

    实际上react render方法返回一个虚拟dom 并没有去执行渲染dom 渲染的过程是交给react 去完成的 这就说明了为什么要在所有数据请求完成后才去实现render 这样做也提高了性能.只调 ...

  3. Azure系列2.1.5 —— BlobOutputStream

    (小弟自学Azure,文中有不正确之处,请路过各位大神指正.) 网上azure的资料较少,尤其是API,全是英文的,中文资料更是少之又少.这次由于公司项目需要使用Azure,所以对Azure的一些学习 ...

  4. day 7-5 守护线程

    一. 守护线程 无论是进程还是线程,都遵循:守护进程(线程)会等待主进程(线程)运行完毕后被销毁. 需要强调的是:运行完毕并非终止运行. 1.对主进程来说,运行完毕指的是主进程代码运行完毕. 2.对主 ...

  5. PMP三点

    三点估算:悲观36天,可能21天,乐观6天.在16至26天内完成的概率是多少?这个算法是PERT估算最终估算结果=(悲观工期+乐观工期+4×最可能工期)/6=(36+6++4*21)/6=21标准差= ...

  6. JS --- 如何获取一个对象的类型

    可以清楚的看到  拿到数字 字符串 对象 函数 数组 通过.slice(8,-1) 可以拿到类型的名称 ,可以做你想要的操作 Object.prototype.toString.call(222) & ...

  7. 从主机A得到id_rsa.pub文件,在主机B创建用户danny加入该文件,实现主机A免密登录主机B

    操作步骤: 1.主机A 生成公钥id_rsa.pub文件并导出该文件 root@mytest:~# ssh-keygen Generating public/private rsa key pair. ...

  8. GitHub & OAuth 2.0 & JWT

    GitHub & OAuth 2.0 & JWT https://www.rfcreader.com/#rfc6749 GitHub & OAuth https://www.b ...

  9. vue 響應接口

    全局方式: 增加屬性和set()和get(): vue.set(targname,key,vaule) targname:對象名或者數組名 key:字符串 value:任何值 刪除屬性和set()和g ...

  10. Docker最全教程——从理论到实战

    Docker最全教程——从理论到实战(一) Docker最全教程——从理论到实战(二) Docker最全教程——从理论到实战(三) Docker最全教程——从理论到实战(四) Docker最全教程—— ...