换markdown写了。。

题意:

给你一个1e5的字符串,1e5组询问,求\([l_1,r_1]\)的所有子串与\([l_2,r_2]\)的lcp

思路:

首先可以发现答案是具有单调性的,我们考虑二分答案,二分的范围显然为\([0,min(r_2-l_2+1,r_1-l_1+1)]\)

对于二分到的字符串长度mid,可以知道它的开头一定在\([l_1,r_1-mid+1]\)中,这样满足了限定条件

于是我们可以通过检查\([l_1,r_1-mid+1]\)中是否有个值p,使得\(lcp(rk[p],rk[l_2]) \geq mid\)即可判断答案

法一:

由\(lcp(i,j)=min(height[k]),i+1\leq k \leq j\)(i,j为排名)可知

为了使\(lcp(rk[p],rk[l2])\)尽量满足条件,rk[p]一定是目标区间内,\(rk[l_2]\)的前驱或后继

这样我们可以对(i,rk[i])建主席树,每次check查询\([l_1,r_1-mid+1]\)里\(rk[l_2]\)的前驱后继

但是问题来了,主席树查询前驱后继会退化。。(我乱讲的,可能是我不会写)

虽然在bzoj上跑了16s过了,但是这显然不优秀

update:退化的问题已经解决了:权值线段树剪枝的误解--以HDU6703为例

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<deque>
#include<set>
#include<vector>
#include<map> #define fst first
#define sc second
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lc root<<1
#define rc root<<1|1
#define lowbit(x) ((x)&(-x)) using namespace std; typedef double db;
typedef long double ldb;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PI;
typedef pair<ll,ll> PLL; const db eps = 1e-6;
const int mod = 1e9+7;
const int maxn = 2e5+100;
const int maxm = 2e6+100;
const int inf = 0x3f3f3f3f;
const db pi = acos(-1.0); int n,m;
char s[maxn];
int sa[maxn],rk[maxn],height[maxn];
int y[maxn],x[maxn],c[maxn];
void getSa(){
for(int i=1;i<=n;i++)++c[x[i]=s[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num = 0;
for(int i=n-k+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)++c[x[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=1;
num=1;
for(int i=2;i<=n;i++){
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
}
if(num==n)break;
m=num;
}
}
void getHeight(){
int k=0;
for(int i=1; i<=n; ++i)rk[sa[i]]=i;
for(int i=1; i<=n; ++i){
if(rk[i]==1) continue;
if(k)--k;
int j=sa[rk[i]-1];
while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])++k;
height[rk[i]]=k;
}
}
int d[maxn][26];
void init(){
for(int i = 1; i <= n; i++) d[i][0]=height[i];
for(int j = 1; (1<<j) <= n; j++){
for(int i = 1; i + (1<<j) - 1 <= n; i++){
d[i][j] = min(d[i][j-1], d[i + (1<<(j-1))][j-1]);
}
}
}
int rmq(int l, int r){
if(l>r)return -1;
int k = 0;
while((1<<(k+1)) <= r-l+1)k++;
return min(d[l][k], d[r-(1<<k)+1][k]);
}
int q; int tot,root[maxn];
int dat[maxn*40],ls[maxn*40],rs[maxn*40];
int insert(int now, int l, int r, int x, int val){
int p = ++tot;
dat[p]=dat[now];ls[p]=ls[now];rs[p]=rs[now];
if(l==r){dat[p]+=val;return p;}
int mid = (l+r)>>1;
if(x<=mid)ls[p]=insert(ls[now],l,mid,x,val);
else rs[p]=insert(rs[now],mid+1,r,x,val);
dat[p]=dat[ls[p]]+dat[rs[p]];
return p;
}
int askmx(int x, int y, int l, int r, int k){//return rk|-1
int sumr = dat[rs[y]]-dat[rs[x]];
int mid = (l+r)>>1;
if(dat[y]-dat[x]==0)return -1;
if(l==r) return l;
if(mid<k){
int lRes = askmx(rs[x],rs[y],mid+1,r,k);
if(lRes==-1)lRes=askmx(ls[x],ls[y],l,mid,k);
return lRes;
}
else return askmx(ls[x],ls[y],l,mid,k); }
int askmi(int x, int y, int l, int r, int k){
int suml = dat[ls[y]]-dat[ls[x]];
int mid = (l+r)>>1;
if(dat[y]-dat[x]==0)return -1;
if(l==r)return l;
if(mid>k){
int rRes=askmi(ls[x],ls[y],l,mid,k);
if(rRes==-1)rRes=askmi(rs[x],rs[y],mid+1,r,k);
return rRes;
}
else return askmi(rs[x],rs[y],mid+1,r,k);
}
bool ck(int x, int l1, int r1, int l2, int r2){
int L = askmx(root[l1-1],root[r1-x+1],1,n,rk[l2]);
int R = askmi(root[l1-1],root[r1-x+1],1,n,rk[l2]);
if(L!=-1)L=rmq(L+1,rk[l2]);
if(R!=-1)R=rmq(rk[l2]+1,R);
return (L>=x||R>=x);
}
int main() {
tot=0;
scanf("%d %d", &n, &q);
scanf("%s",s+1);
m=122;n=strlen(s+1);
getSa();
getHeight();
init();
for(int i = 1; i <= n; i++){
root[i]=insert(root[i-1],1,n,rk[i],1);
}
init();
while(q--){
int l1,r1,l2,r2;
scanf("%d %d %d %d", &l1, &r1,&l2, &r2);
int ans = 0;
if(l2>=l1&&l2<=r1)ans=min(r2-l2+1,r1-l2+1);
int l = 0, r = min(r1-l1+1,r2-l2+1);
int res = 0;
while(l<=r){
int mid = (l+r)>>1;
if(ck(mid,l1,r1,l2,r2)){
l=mid+1;
res=mid;
}
else r=mid-1;
}
ans=max(ans,res);
printf("%d\n",ans);
}
return 0;
}
/*
8 1
ththhtht
1 6 7 8 85 292 31 259
79 299 5 232 8 10
aababbab
1 1 1 1
1 2 4 5
1 5 4 5
2 3 4 5
1 5 2 5
1 6 7 8
3 6 1 7
4 4 3 8
1 5 1 8
2 4 5 8
*/

法二:

既然\(lcp(i,j)\)对于i向左以及j向右都是单调不增的,我们可以二分出rk的左右边界L以及R,使得\(lcp(i,rk[l_2])\geq mid\)

然后我们对于(i,sa[i])建可持久化线段树,查询区间\((L+1,rk[l2])\)中是否有坐标在\([l_1,r_1-mid+1]\)中

这样复杂度是完美的\(O(nlog^2n)\)

代码:

我好懒,没写代码

BZOJ 4556(后缀数组+主席树求前驱后继+二分||后缀数组+二分+可持久化线段树)的更多相关文章

  1. Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

    题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB    总提交次数:196   AC次数:65   平均分: ...

  2. BZOJ 2653 middle (可持久化线段树+中位数+线段树维护最大子序和)

    题意: 左端点在[a,b],右端点在[c,d],求这个线段里中位数(上取整)最大值 思路: 对数组离散化,对每一个值建中位数的可持久化线段树(有重复也没事),就是对于root[i],大于等于i的值为1 ...

  3. 【洛谷 P3834】 可持久化线段树1(主席树)

    题目链接 主席树=可持久化权值线段树. 如果你不会可持久化线段树,请右转 如果你不会权值线段树,请自行脑补,就是线段树维护值域里有多少个数出现. 可持久化线段树是支持查询历史版本的. 我们对每个数都进 ...

  4. 【bzoj2653】middle 可持久化线段树区间合并

    题目描述 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整.给你一个长度为n的序列s.回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[ ...

  5. [DP][SA][可持久化线段树]黑红兔

    源自 xyz32768 菜鸡的 FJ 省冬令营模拟赛题 原题 CF1063F Statement 给定一个长度为 \(n\) 的字符串 \(s\),仅包含小写英文字母 要从中从左往右选出若干段不相交的 ...

  6. 【BZOJ-3653】谈笑风生 DFS序 + 可持久化线段树

    3653: 谈笑风生 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 628  Solved: 245[Submit][Status][Discuss] ...

  7. [bzoj3207]花神的嘲讽计划Ⅰ[可持久化线段树,hash]

    将每k个数字求一个哈希值,存入可持久化线段树,直接查询即可 #include <iostream> #include <algorithm> #include <cstd ...

  8. 主席树||可持久化线段树+离散化 || 莫队+分块 ||BZOJ 3585: mex || Luogu P4137 Rmq Problem / mex

    题面:Rmq Problem / mex 题解: 先离散化,然后插一堆空白,大体就是如果(对于以a.data<b.data排序后的A)A[i-1].data+1!=A[i].data,则插一个空 ...

  9. luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

    luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include< ...

随机推荐

  1. 用ES5实现ES6的数组方法map

    先举个常见的栗子: var arr = [1,2,3,4,6,7,8,9,12,3,25,63,100] var arr2 = arr.map(item => item += 1) consol ...

  2. VS Code配置C/C++环境

    VS Code配置C/C++环境 一.下载和安装VS Code 1.访问VS Code官网下载安装包 2.安装VS Code 3. 安装后, 打开VS Code是英文,按住Ctrl+shift+x进入 ...

  3. GeneXus 16 如何实现自动化测试和发布

    CI/CD(持续集成/持续发布)是一种软件开发策略,以使公司能够尽可能快速.高效地给客户发布新功能.为了能够实现CI/CD,就需要通过PipeLine对整个软件过程进行一系列的节点管理,必须将每个阶段 ...

  4. must appear in the GROUP BY clause or be used in an aggregate function

    今天在分组统计的时候pgsql报错 must appear in the GROUP BY clause or be used in an aggregate function,在mysql里面是可以 ...

  5. Java Collection集合中的iterator方法

    Iterator接口的概述 /** * java.util.Iterator接口:选代器(对集合进行遍历) * 有两个常用的方法 * boolean hasNext() * 如果仍有元素可以迭代,则返 ...

  6. Spring-cloud微服务实战【二】:eureka注册中心(上)

    ## 前言   本系列教程旨在为大家演示如何一步一步构建一整套微服务系统,至于其中的数据库用什么,订单ID如何保持唯一,分布式相关问题等等不在我们讨论范围内,本教程为了方便大家后续下载代码运行测试,不 ...

  7. 【Linux】---Linux系统下各种常用命令总结

    在Linux系统下,“万物皆文件”,之所以强调在强调这个概念,是因为很多人已经习惯了win系统下找找点点得那种方式和思维,因此总是会觉得linux系统下很多指令既复杂又难记.其实都是一样得东西,只是w ...

  8. python 栈

    栈的特点:先进后出 class Stack: def __init__(self): self.data = [] def push(self, val): self.data.append(val) ...

  9. mysql 注入问题

    1.实质 MySql语句是用户自行拼接的字符串 2.例子 import pymysql # 获取用户输入信息 username = input("请输入用户名:") pwd = i ...

  10. 8.JavaSE之变量、常量、作用域

    变量variable: 变量是什么:就是内存中开辟的可以变化的量! Java是一种强类型语言,每个变量都必须声明其类型. Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型,作用域   ...