题面传送门

一道码农题…………

u1s1 感觉这类题目都挺套路的,就挑个有代表性的题写一篇题解罢。

首先注意到答案满足可二分性,故考虑二分答案 \(mid\),转化为判定性问题。

考虑怎样检验 \(mid\) 是否可行,它等价于是否存在 \(s[a...b]\) 中的一个子串 \(t\) 满足 \(s[c...c+mid-1]\) 为 \(t\) 的前缀。不过不难发现这个“前缀”是假的,因为 \(\forall a\le l\le r\le b\),\(s[l...r]\) 的任意一个前缀都是 \(s[a...b]\) 的子串,故只需检验 \(s[c...c+mid-1]\) 是否为 \(s[a...b]\) 的子串。而根据 LCP Lemma 的知识可知这个东西又可以转化为 \(\exist x\in[a,b-mid+1]\) 使得 \(\text{LCP}(x,c)\geq mid\)。

很明显满足 \(\text{LCP}(x,c)\geq mid\) 的 \(x\) 在字典序上一定是一段连续的区间 \([L,R]\),这个区间可以通过二分+ST 表求出,因此 \(\exist x\in[a,b-mid+1],\text{LCP}(x,c)\ge mid\Leftrightarrow\exist i\in[L,R],sa_i\in[a,b-mid+1]\)。考虑以后缀编号为下标建立主席树,第 \(i\) 棵树上 \([l,r]\) 的区间中表示在字典序前 \(i\) 的后缀中有多少个属于区间 \([l,r]\),然后在编号 \(R,L-1\) 为下标的两棵主席树上查询下标为 \([a,b-mid+1]\) 中的数之和并相减,判断是否 \(>0\) 即可。

时间复杂度 \(\mathcal O(n\log^2n)\)。常数巨大……

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=2e5;
const int MAXP=MAXN*40;
const int LOG_N=18;
int n,qu;char s[MAXN+5];pii x[MAXN+5];
int sa[MAXN+5],rk[MAXN+5],buc[MAXN+5],seq[MAXN+5],ht[MAXN+5];
int st[MAXN+5][LOG_N+2];
void getsa(){
int vmax=122,gr=0;
for(int i=1;i<=n;i++) buc[s[i]]++;
for(int i=1;i<=vmax;i++) buc[i]+=buc[i-1];
for(int i=n;i;i--) sa[buc[s[i]]--]=i;
for(int i=1;i<=n;i++){
if(s[sa[i]]!=s[sa[i-1]]) gr++;
rk[sa[i]]=gr;
} vmax=gr;
for(int k=1;k<=n;k<<=1){
for(int i=1;i<=n;i++){
if(i+k<=n) x[i]=mp(rk[i],rk[i+k]);
else x[i]=mp(rk[i],0);
} memset(buc,0,sizeof(buc));gr=0;int num=0;
for(int i=n-k+1;i<=n;i++) seq[++num]=i;
for(int i=1;i<=n;i++) if(sa[i]>k) seq[++num]=sa[i]-k;
for(int i=1;i<=n;i++) buc[x[i].fi]++;
for(int i=1;i<=vmax;i++) buc[i]+=buc[i-1];
for(int i=n;i;i--) sa[buc[x[seq[i]].fi]--]=seq[i];
for(int i=1;i<=n;i++){
if(x[sa[i]]!=x[sa[i-1]]) gr++;
rk[sa[i]]=gr;
} vmax=gr;if(vmax==n) break;
}
}
void getht(){
int k=0;
for(int i=1;i<=n;i++){
if(rk[i]==1) continue;if(k) k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
ht[rk[i]]=k;
}
}
void buildst(){
for(int i=1;i<=n;i++) st[i][0]=ht[i];
for(int i=1;i<=LOG_N;i++) for(int j=1;j+(1<<i)-1<=n;j++)
st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]);
}
int query(int l,int r){
// printf("%d %d\n",l,r);
int k=log2(r-l+1);
return min(st[l][k],st[r-(1<<k)+1][k]);
}
namespace segtree{
struct node{int ch[2],val;} s[MAXP+5];
int ncnt,rt[MAXN+5];
void build(int &k,int l,int r){
k=++ncnt;if(l==r) return;int mid=l+r>>1;
build(s[k].ch[0],l,mid);build(s[k].ch[1],mid+1,r);
}
int modify(int k,int l,int r,int p,int x){
int z=++ncnt;s[z]=s[k];s[z].val+=x;
if(l==r) return z;
int mid=l+r>>1;
if(p<=mid) s[z].ch[0]=modify(s[k].ch[0],l,mid,p,x);
else s[z].ch[1]=modify(s[k].ch[1],mid+1,r,p,x);
return z;
}
int query(int k,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return s[k].val;
int mid=(l+r)>>1;
if(qr<=mid) return query(s[k].ch[0],l,mid,ql,qr);
else if(ql>mid) return query(s[k].ch[1],mid+1,r,ql,qr);
else return query(s[k].ch[0],l,mid,ql,mid)+query(s[k].ch[1],mid+1,r,mid+1,qr);
}
}
using segtree::rt;
using segtree::modify;
using segtree::build;
bool check(int x,int a,int b,int c){
b=b-x+1;
int L=1,R=rk[c]-1,mid,l=rk[c],r=rk[c];
while(L<=R) (query((mid=L+R>>1)+1,rk[c])>=x)?l=mid,R=mid-1:L=mid+1;
L=rk[c]+1,R=n;
while(L<=R) (query(rk[c]+1,mid=L+R>>1)>=x)?r=mid,L=mid+1:R=mid-1;
// printf("%d %d %d %d %d\n",c,x,l,r,segtree::query(rt[r],1,n,a,b)-segtree::query(rt[l-1],1,n,a,b));
return segtree::query(rt[r],1,n,a,b)-segtree::query(rt[l-1],1,n,a,b);
}
int main(){
scanf("%d%d%s",&n,&qu,s+1);
getsa();getht();buildst();build(rt[0],1,n);
// printf("%d\n",query(8,9));
// for(int i=1;i<=n;i++) printf("%d\n",sa[i]);
for(int i=1;i<=n;i++) rt[i]=modify(rt[i-1],1,n,sa[i],1);
while(qu--){
int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
int l=0,r=min(b-a+1,d-c+1),mid,x=0;
while(l<=r) check(mid=l+r>>1,a,b,c)?x=mid,l=mid+1:r=mid-1;
printf("%d\n",x);
}
return 0;
}
/*
13 1
ababbabbbaaab
2 4 8 10
*/

洛谷 P4094 [HEOI2016/TJOI2016]字符串(SA+主席树)的更多相关文章

  1. 洛谷P2824 [HEOI2016/TJOI2016]排序(线段树)

    传送门 这题的思路好清奇 因为只有一次查询,我们考虑二分这个值为多少 将原序列转化为一个$01$序列,如果原序列上的值大于$mid$则为$1$否则为$0$ 那么排序就可以用线段树优化,设该区间内$1$ ...

  2. 洛谷 P2824 [HEOI2016/TJOI2016]排序 (线段树合并)

    (另外:题解中有一种思路很高妙而且看上去可以适用一些其他情况的离线方法) 线段树合并&复杂度的简单说明:https://blog.csdn.net/zawedx/article/details ...

  3. 洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP

    洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他. 玩具上有一个数列,数列中某些项的值可能会 ...

  4. P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案

    $ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...

  5. BZOJ4553/洛谷P4093 [HEOI2016/TJOI2016]序列 动态规划 分治

    原文链接http://www.cnblogs.com/zhouzhendong/p/8672434.html 题目传送门 - BZOJ4553 题目传送门 - 洛谷P4093 题解 设$Li$表示第$ ...

  6. 洛谷 P4091 [HEOI2016/TJOI2016]求和 解题报告

    P4091 [HEOI2016/TJOI2016]求和 题目描述 在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心. 现在他想计算这样一个函数的值: \[ f(n)=\sum_{i=0}^n\ ...

  7. 洛谷 P4093 [HEOI2016/TJOI2016]序列 解题报告

    P4093 [HEOI2016/TJOI2016]序列 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一 ...

  8. 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告

    P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...

  9. BZOJ4556 HEOI2016/TJOI2016字符串 (后缀树+主席树)

    二分答案后相当于判断一个区间的后缀与某个后缀的最长公共前缀是否能>=ans.建出后缀树,在上述问题中后者所在节点向上倍增的跳至len>=ans的最高点,然后相当于查询子树中是否有该区间的节 ...

随机推荐

  1. 【MySQL】MySQL(四)存储引擎、索引、锁、集群

    MySQL存储引擎 MySQL体系结构 体系结构的概念 任何一套系统当中,每个部件都能起到一定的作用! MySQL的体系结构 体系结构详解 客户端连接 支持接口:支持的客户端连接,例如C.Java.P ...

  2. pycharm安装第三方库

    https://jingyan.baidu.com/article/4853e1e54b845e1909f7268f.html

  3. Takin Talks·上海 |开源后首场主题研讨会来了,一起解密Takin技术吧!

      自 6 月 25 日全球首款生产环境全链路压测平台 Takin 正式开源,短短 13 天时间,Github 主页上 Star 数已超过 730,开发者社群也积累了 1500+粉丝.群内技术研讨氛围 ...

  4. MySQL:补充知识

    MySQL补充知识 在学习完 MySQL 基础与提高内容后: 基础知识笔记: MySQL:基础语法-1 MySQL:基础语法-2 MySQL:基础语法-3 MySQL:基础语法-4 提高知识笔记: M ...

  5. Java编程开发学习路线图(附所有免费课程+在线自测)

    转自  https://yq.aliyun.com/articles/134286?spm=5176.100239.0.0.1UfveS 摘要: 长期以来,Java一直占据TIOBE编程语言排行版第一 ...

  6. Scrum Meeting 12

    第12次例会报告 日期:2021年06月03日 会议主要内容概述: 介绍了现有进度,wpb介绍了jwt的用法以及部署的swagger的测试用法. 一.进度情况 我们采用日报的形式记录每个人的具体进度, ...

  7. BUAA-OO-JML

    BUAA-OO-JML JML 概念与 toolchain JML 是一种为 Java 程序设计的.遵循 design by contract 范式的.基于 Hoare Logic 构建的 behav ...

  8. 无网络下,配置yum本地源

    1. 新建一个没有iso镜像文件的虚拟机: 2. 本地上传一个镜像文件(CentOS7的镜像),到虚拟机已创建的目录: 例如:上传一个镜像文件CentOS-7-x86_64-Everything-17 ...

  9. 洛谷 P5665 [CSP-S2019] 划分

    链接: P5665 题意: 给出 \(n\) 个整数 \(a_i\) ,你需要找到一些分界点 \(1 \leq k_1 \lt k_2 \lt \cdots \lt k_p \lt n\),使得 \( ...

  10. atcoder ABC233

    B 题意 给一个字符串, 可以把第一个字母移到最后, 也可以把最后一个字母放第一个, 问字典序最大最小的字符串. 题解 把第一个放最后一个, 相当于把最后一个放第一个执行n-1次, 那么我们不妨只进行 ...