洛谷 P4094 [HEOI2016/TJOI2016]字符串(SA+主席树)
一道码农题…………
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+主席树)的更多相关文章
- 洛谷P2824 [HEOI2016/TJOI2016]排序(线段树)
传送门 这题的思路好清奇 因为只有一次查询,我们考虑二分这个值为多少 将原序列转化为一个$01$序列,如果原序列上的值大于$mid$则为$1$否则为$0$ 那么排序就可以用线段树优化,设该区间内$1$ ...
- 洛谷 P2824 [HEOI2016/TJOI2016]排序 (线段树合并)
(另外:题解中有一种思路很高妙而且看上去可以适用一些其他情况的离线方法) 线段树合并&复杂度的简单说明:https://blog.csdn.net/zawedx/article/details ...
- 洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP
洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他. 玩具上有一个数列,数列中某些项的值可能会 ...
- P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案
$ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...
- BZOJ4553/洛谷P4093 [HEOI2016/TJOI2016]序列 动态规划 分治
原文链接http://www.cnblogs.com/zhouzhendong/p/8672434.html 题目传送门 - BZOJ4553 题目传送门 - 洛谷P4093 题解 设$Li$表示第$ ...
- 洛谷 P4091 [HEOI2016/TJOI2016]求和 解题报告
P4091 [HEOI2016/TJOI2016]求和 题目描述 在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心. 现在他想计算这样一个函数的值: \[ f(n)=\sum_{i=0}^n\ ...
- 洛谷 P4093 [HEOI2016/TJOI2016]序列 解题报告
P4093 [HEOI2016/TJOI2016]序列 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一 ...
- 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告
P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...
- BZOJ4556 HEOI2016/TJOI2016字符串 (后缀树+主席树)
二分答案后相当于判断一个区间的后缀与某个后缀的最长公共前缀是否能>=ans.建出后缀树,在上述问题中后者所在节点向上倍增的跳至len>=ans的最高点,然后相当于查询子树中是否有该区间的节 ...
随机推荐
- 2 What is the Domain Driven Design? 什么是领域驱动设计
What is the Domain Driven Design? 什么是领域驱动设计 Domain-driven design (DDD) is an approach to software de ...
- 对比7种分布式事务方案,还是偏爱阿里开源的Seata,真香!(原理+实战)
前言 这是<Spring Cloud 进阶>专栏的第六篇文章,往期文章如下: 五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强? openFeign夺命连环9问,这谁受得了? 阿里面 ...
- [敏捷软工团队博客]Beta阶段发布声明
项目 内容 2020春季计算机学院软件工程(罗杰 任健) 博客园班级博客 作业要求 Beta阶段发布声明 我们在这个课程的目标是 在团队合作中锻炼自己 这个作业在哪个具体方面帮助我们实现目标 对Bet ...
- 微服务(三) Eureka注册中心和Ribbon负载均衡
1. Eureka注册中心 1.1 Eureka的结构和作用 在上一篇文章中 微服务(二)服务拆分及远程调用 order-service在发起远程调用的时候,该如何得知user-service实例的i ...
- es7集群的搭建
es7集群的搭建 一.需求 二.前置条件 三.搭建步骤 1.下载es 2.创建 `es` 用户并给es目录授权 3.修改es的配置文件 4.es01目录下 elasticsearch.yml 的一个完 ...
- Noip模拟75 2021.10.12
T1 如何优雅的送分 他说是送分题,我就刚,没刚出来,想到莫比乌斯容斥后就都没推出来 好吧还是不能被恶心的题目,挑衅的语言打乱做题节奏 于是这一场也就没了.... $F(i)$表示$i$的不同质因子集 ...
- 2021.8.24考试总结[NOIP47]
T1 prime 发现只需筛小于等于$mid(\sqrt r,k)$的质数,之后用这些质数筛掉区间内不合法的数即可. $code:$ 1 #include<bits/stdc++.h> 2 ...
- 【做题记录】CF1444A Division
CF1444A Division 题意: 给定 \(t\) 组询问,每组给两个数 \(p_i\) 和 \(q_i\) ,找出最大的整数 \(x_i\) ,要求 \(p_i\) 可被 \(x_i\) 整 ...
- [转]DDR相关的一些基础知识
ODT ( On-DieTermination ,片内终结)ODT 也是 DDR2 相对于 DDR1 的关键技术突破,所谓的终结(端接),就是让信号被电路的终端吸 收掉,而不会在电路上形成反射, 造成 ...
- vs2015 MSB600 "inf2cat.ext"已退出,代码为2
使用vs2015编译XDMA驱动过程中,报如下错误: vs2015 MSB600 "inf2cat.ext"已退出,代码为2 在使用Qt编译PCIE码表的过程中,出现C1038:无 ...