题目

首先先对\(s\)建一个\(\operatorname{SAM}\),设\(w=kq\)

发现\(k,q\leq 10^5\),但是\(w\leq 10^5\),于是套路地根号讨论一下

如果\(k\leq \sqrt{w}\),那么说明单个询问串都很短,我们完全可以暴力长度为\(k\)的字符串的所有子串,之后二分查询一下这个子串在\([a,b]\)的询问里出现的次数,这个子串在\(s\)出现的次数直接边走边查就好了,这边的复杂度是\(O(qk^2\log m)=O(w\sqrt{w}\log m)\)

如果\(k>\sqrt{w}\),那么说明单个询问串很长,但是询问个数\(q\leq \sqrt{w}\),于是我们直接扫\([a,b]\)范围内的所有区间,考虑到子串\([l,r]\)在\(s\)中出现了多少次可以转化为\(s\)有多少个前缀和前缀\(r\)的\(\operatorname{LCS}\)长度不小于\(r-l+1\),于是直接树上倍增求解,复杂度\(O(qm\log n)=O(m\sqrt{w}\log n)\)

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=2e5+5;
int fa[maxn],A[maxn],tax[maxn],son[maxn][26],len[maxn],sz[maxn];
int n,m,k,q,lst,cnt;
char S[maxn>>1];
inline void ins(int c) {
int p=++cnt,f=lst;lst=p;
len[p]=len[f]+1,sz[p]=1;
while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
if(!f) {fa[p]=1;return;}
int x=son[f][c];
if(len[f]+1==len[x]) {fa[p]=x;return;}
int y=++cnt;
len[y]=len[f]+1,fa[y]=fa[x],fa[p]=fa[x]=y;
for(re int i=0;i<26;i++) son[y][i]=son[x][i];
while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
namespace sub1 {
int id[350][350],tot;
std::vector<int> v[maxn>>1];
inline int find(int t,int x) {
int l=0,r=v[t].size()-1,h=-1;
while(l<=r) {
int mid=l+r>>1;
if(v[t][mid]<=x) h=mid,l=mid+1;
else r=mid-1;
}
return h+1;
}
void solve() {
for(re int i=1;i<=k;++i)
for(re int j=i;j<=k;++j) id[i][j]=++tot;
for(re int x,y,i=1;i<=m;i++) {
x=read()+1,y=read()+1;
v[id[x][y]].push_back(i);
}
for(re int x,y,i=1;i<=q;i++) {
scanf("%s",S+1);x=read(),y=read()+1;
LL ans=0;
for(re int now=1,j=1;j<=k;++j,now=1)
for(re int p=j;p<=k;++p) {
now=son[now][S[p]-'a'];
if(!now) break;
ans+=1ll*sz[now]*(find(id[j][p],y)-find(id[j][p],x));
}
printf("%lld\n",ans);
}
}
}
namespace sub2 {
int ql[maxn>>1],qr[maxn>>1],deep[maxn];
int lg[maxn>>1],pos[maxn>>1],ml[maxn>>1],f[18][maxn];
void solve() {
for(re int i=1;i<=m;i++) ql[i]=read()+1,qr[i]=read()+1;
deep[1]=1;for(re int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(re int i=2;i<=cnt;i++) deep[A[i]]=deep[fa[A[i]]]+1;
for(re int i=1;i<=cnt;i++) {
int x=A[i];f[0][x]=fa[x];
for(re int j=1;j<=lg[deep[x]];++j)
f[j][x]=f[j-1][f[j-1][x]];
}
int x,y;
while(q--) {
scanf("%s",S+1);x=read()+1,y=read()+1;
int now=1,l=0;LL ans=0;
for(re int i=1;i<=k;i++) {
if(son[now][S[i]-'a']) {
pos[i]=now=son[now][S[i]-'a'];
ml[i]=++l;continue;
}
while(now&&!son[now][S[i]-'a']) now=fa[now];
if(!now) pos[i]=now=1,ml[i]=l=0;
else ml[i]=l=len[now]+1,pos[i]=now=son[now][S[i]-'a'];
}
for(re int i=x;i<=y;++i) {
if(ml[qr[i]]<qr[i]-ql[i]+1) continue;
now=pos[qr[i]];
for(re int j=lg[deep[now]];j>=0;--j)
if(len[f[j][now]]>=qr[i]-ql[i]+1) now=f[j][now];
ans+=sz[now];
}
printf("%lld\n",ans);
}
}
}
int main() {
n=read(),m=read(),q=read(),k=read();
scanf("%s",S+1);lst=cnt=1;
for(re int i=1;i<=n;i++) ins(S[i]-'a');
for(re int i=1;i<=cnt;i++) tax[len[i]]++;
for(re int i=1;i<=n;i++) tax[i]+=tax[i-1];
for(re int i=1;i<=cnt;i++) A[tax[len[i]]--]=i;
for(re int i=cnt;i;--i) sz[fa[A[i]]]+=sz[A[i]];
if(k<=std::sqrt(k*q)) sub1::solve();else sub2::solve();
return 0;
}

loj6031「雅礼集训 2017 Day1」字符串的更多相关文章

  1. 并不对劲的Loj6031:「雅礼集训 2017 Day1」字符串

    题目传送门:-> 看到题目的第一反应当然是暴力:对于串s建后缀自动机,每次询问中,求w对应的子串在s的SAM中的right集合.O(qmk)听上去显然过不了. 数据范围有个∑w<=1e5, ...

  2. [LOJ 6031]「雅礼集训 2017 Day1」字符串

    [LOJ 6031] 「雅礼集训 2017 Day1」字符串 题意 给定一个长度为 \(n\) 的字符串 \(s\), \(m\) 对 \((l_i,r_i)\), 回答 \(q\) 个询问. 每个询 ...

  3. loj#6031. 「雅礼集训 2017 Day1」字符串(SAM 广义SAM 数据分治)

    题意 链接 Sol \(10^5\)次询问每次询问\(10^5\)个区间..这种题第一感觉就是根号/数据分治的模型. \(K\)是个定值这个很关键. 考虑\(K\)比较小的情况,可以直接暴力建SAM, ...

  4. 「雅礼集训 2017 Day1」字符串 SAM、根号分治

    LOJ 注意到\(qk \leq 10^5\),我们很不自然地考虑根号分治: 当\(k > \sqrt{10^5}\),此时\(q\)比较小,与\(qm\)相关的算法比较适合.对串\(s\)建S ...

  5. loj 6031「雅礼集训 2017 Day1」字符串

    loj 注意到每次询问串长度都是给定的,并且询问串长\(k*\)询问次数\(q<10^5\),所以这里面一个东西大的时候另一个东西就小,那么考虑对较小的下功夫 如果\(k\le \sqrt{n} ...

  6. 「雅礼集训 2017 Day1」 解题报告

    「雅礼集训 2017 Day1」市场 挺神仙的一题.涉及区间加.区间除.区间最小值和区间和.虽然标算就是暴力,但是复杂度是有保证的. 我们知道如果线段树上的一个结点,\(max=min\) 或者 \( ...

  7. [LOJ 6030]「雅礼集训 2017 Day1」矩阵

    [LOJ 6030] 「雅礼集训 2017 Day1」矩阵 题意 给定一个 \(n\times n\) 的 01 矩阵, 每次操作可以将一行转置后赋值给某一列, 问最少几次操作能让矩阵全为 1. 无解 ...

  8. [LOJ 6029]「雅礼集训 2017 Day1」市场

    [LOJ 6029] 「雅礼集训 2017 Day1」市场 题意 给定一个长度为 \(n\) 的数列(从 \(0\) 开始标号), 要求执行 \(q\) 次操作, 每次操作为如下四种操作之一: 1 l ...

  9. loj#6030. 「雅礼集训 2017 Day1」矩阵(贪心 构造)

    题意 链接 Sol 自己都不知道自己怎么做出来的系列 不难观察出几个性质: 最优策略一定是先把某一行弄黑,然后再用这一行去覆盖不是全黑的列 无解当且仅当无黑色.否则第一个黑色所在的行\(i\)可以先把 ...

随机推荐

  1. JAVA C 数据类型对应

    { Java—C和操作系统数据类型的对应表 Java Type C Type Native Representation boolean int 32-bit integer (customizabl ...

  2. 阿里云重磅推出物联网安全运营中心Link SOC

    阿里云IoT自主研发了新一代物联网安全平台Link Security,面向IoT设备全生命周期构建了一整套全链路多层次的安全防御体系,IoT物联网平台的业务在不同层面可以按需集成安全能力. 1.首先在 ...

  3. (转)mysql分区技术2

    转:http://database.51cto.com/art/201002/184392.htm 非整数列分区 任何使用过分区的人应该都遇到过不少问题,特别是面对非整数列分区时,MySQL 5.1只 ...

  4. windows中创建流量转发规则

    #创建流量转发,其中listenaddress为内网ip(也就是ipconfig中所显示的IP),listenport为监听的本机对外端口,后两个分别是需转发的目标主机IP和端口. netsh int ...

  5. 面向XX程序设计到底是个啥

    面向过程编程:面向(对着)-->过程(流程步骤)-->编程(码代码) IPO是啥 input(输入)-->process(过程处理)-->output(输出) 未来码代码的目的 ...

  6. nio读取文件,输出文件

    io流的一种: package com.cxy.ssp.Automic; import java.io.FileOutputStream; import java.io.IOException; im ...

  7. 03->OpenGL多边形,glut实现三角形条带和三角形扇

    图形学中基本图元是多边形,一般要求是凸多边形,三角形是最简单的凸多边形,在图形渲染中比一般多边形其绘制速度快.今天学习OpenGL绘制三角形条带和三角形扇基础.编程环境! 1. 三角形条带 指定顶点序 ...

  8. 2019-4-29-WPF-如何判断一个控件在滚动条的里面是用户可见

    title author date CreateTime categories WPF 如何判断一个控件在滚动条的里面是用户可见 lindexi 2019-4-29 9:42:2 +0800 2019 ...

  9. JS函数 函数的作用,可以写一次代码,然后反复地重用这个代码。

    什么是函数 函数的作用,可以写一次代码,然后反复地重用这个代码. 如:我们要完成多组数和的功能. var sum; sum = 3+2; alert(sum); sum=7+8 ; alert(sum ...

  10. 百度开平台BAE搭建网站

    百度开平台BAE搭建网站 一.注册:在百度云注册账号,并且登陆 然后实名验证 二.开始搭建 三.部署项目:我们来把我们的项目提交上去 填写百度云的账号密码 四.删除:删除部署项目 以上就是百度开平台B ...