题面传送门

一道码农题…………

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. UltraSoft - Beta - Scrum Meeting 10

    Date: May 26th, 2020. Scrum 情况汇报 进度情况 组员 负责 今日进度 q2l PM.后端 记录Scrum Meeting Liuzh 前端 暂无 Kkkk 前端 前端增加了 ...

  2. Linux基础入门级命令文档

    Linux系统上命令的使用格式,及常用命令示例 1.命令提示符 登录系统后,第一眼看到的内容是: [root@node01 ~]# 上图就是 Linux 系统的命令提示符.那么,这个提示符的含义是什么 ...

  3. Python | 标识符命名规范

    简单地理解,标识符就是一个名字,就好像我们每个人都有属于自己的名字,它的主要作用就是作为变量.函数.类.模块以及其他对象的名称. Python 中标识符的命名不是随意的,而是要遵守一定的命令规则,比如 ...

  4. stat命令的实现

    任务详情 学习使用stat(1),并用C语言实现 提交学习stat(1)的截图 man -k ,grep -r的使用 伪代码 产品代码 mystate.c,提交码云链接 测试代码,mystat 与st ...

  5. vim 删除 替换

    1,删除包含pattern的某一行 :g/pattern/d 或 :%g/pattern/d 2,删除不包含pattern的某一行 :v/pattern/d 或 :g!/pattern/d 3,替换 ...

  6. Python ValueError: Attempted relative import in non-package Relative import相对引用 错误

    包含相对路径import的python脚本不能直接运行,只能作为module被引用. 例如 from . import mod1 有这样代码的文件只能最为moulule为不能直接运行.相对路径就是相对 ...

  7. Luogu P1023 [NOIp2000提高组]税收与补贴问题 | 数学

    题目链接 思路:列不等式组,然后解出不等式,得出答案的取值范围,最后取一个绝对值最小的答案就行了. #include<iostream> #include<cstdio> #i ...

  8. Docker的centos镜像内无法使用systemctl命令的解决办法

    在Docker官方的centos镜像内无法使用systemctl命令的解决办法, 使用该命令docker报错 Failed to get D-Bus connection: Operation not ...

  9. .Net Minimal Api 介绍

    Minimal API是.Net 6中新增的模板,借助C# 10的一些特性以最少的代码运行一个Web服务.本文脱离VS通过VS Code,完成一个简单的Minimal Api项目的开发. 创建项目 随 ...

  10. CLion 2021.2 debug报错 process exited with status -1 (attach failed (Not allowed to attach to process.

    Clion 升级 2021.2 版本后 debug 报错: process exited with status -1 (attach failed (Not allowed to attach to ...