luogu

对于每个询问从大到小枚举长度,哈希判断是否合法,AC

假的(指数据)

考虑发掘border的限制条件,如果一个border的前缀部分的末尾位置位置\(x(l\le x < r)\)满足\(s[l,x]=s[r-(x-l+1)+1,r]\),那么要满足最长公共后缀\(lcs(x,r)\ge x-l+1\)

而\(lcs(x,y)\)是\(x,y\)两个点在串\(S\)的sam的\(parent\)树上的lca的\(length\),所以先把\(S\)的sam建出来,那么询问\((l,r)\)就是对于\(r\)的对应点\(u\),最大的\(x\in [l,r)\)满足\(x\)对应的点\(v\)使得\(length_{lca(u,v)}\ge x-l+1\).这个可以看成\(u\)点子树内的信息和到根路径上每个点的儿子子树信息

先考虑子树信息,可以对每个点搞个线段树出来维护子树内的后缀对应点,每次在线段树上二分找最大的满足条件的\(x\)

然后是后者.考虑把一个点子树信息全部挂在点上(就算是问到前面问到过的信息也没事,因为前面算的信息在更深的地方计算一定更优),然后就是一个链上线段树信息的询问.考虑树链剖分,每次询问\(log\)条重链上线段树的信息.注意到询问的部分都是若干条重链的前缀,所以可以dfs,维护出每个点到重链顶端的线段树信息,这里的话要使用可持久化线段树

当然这样还不太优秀.我们可以把每个询问依次挂到从那个点跳到根时每条轻边的顶端,这样在dfs时只要维护一个重链的线段就好了.注意每个点到根会经过\(log\)条重链,所以轻链信息也可以挂到到根路径上每条轻边的顶端,每次把信息往线段树里插就行.还有如果处理完一个子树后,如果它到父亲的边是轻边,要清空他的线段树节点

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double using namespace std;
const int N=2e5+10;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int n,q,qr[N][2],an[N];
char cc[N];
int s[N*50],tg[N*50],ch[N*50][2],rt[N<<1],tot=1;
void inst(int o1,int o2,int x,int y)
{
int l=1,r=n;
s[o1]=min(s[o2],y);
while(l<r)
{
int mid=(l+r)>>1;
if(x<=mid)
{
ch[o1][0]=++tot,ch[o1][1]=ch[o2][1];
o1=ch[o1][0],o2=ch[o2][0];
r=mid;
}
else
{
ch[o1][0]=ch[o2][0],ch[o1][1]=++tot;
o1=ch[o1][1],o2=ch[o2][1];
l=mid+1;
}
s[o1]=min(s[o2],y);
}
}
int merge(int o1,int o2)
{
if(!o1||!o2) return o1+o2;
s[o1]=min(s[o1],s[o2]);
ch[o1][0]=merge(ch[o1][0],ch[o2][0]);
ch[o1][1]=merge(ch[o1][1],ch[o2][1]);
return o1;
}
int quer(int o,int l,int r,int ll,int rr,int x)
{
if(!o) return 0;
if(l==r) return s[o]<=x?l:0;
int mid=(l+r)>>1;
if(ll<=l&&r<=rr)
{
if(ch[o][1]&&s[ch[o][1]]<=x) return quer(ch[o][1],mid+1,r,ll,rr,x);
return quer(ch[o][0],l,mid,ll,rr,x);
}
int an=0;
if(rr>mid) an=quer(ch[o][1],mid+1,r,ll,rr,x);
if(an) return an;
return quer(ch[o][0],l,mid,ll,rr,x);
}
vector<int> qs[N<<1],e[N<<1],op[N<<1];
int fa[N<<1],len[N<<1],to[N<<1][26],sz[N<<1],la=1,tt=1,ps[N];
int ss[N<<1],hs[N<<1],top[N<<1],px[N<<1];
void extd(int cx,int ii)
{
int np=++tt,p=la;
len[np]=len[p]+1,sz[np]=1,ps[ii]=np,px[np]=ii,la=tt;
while(!to[p][cx]) to[p][cx]=np,p=fa[p];
if(!p) fa[np]=1;
else
{
int q=to[p][cx];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tt;
fa[nq]=fa[q],len[nq]=len[p]+1;
memcpy(to[nq],to[q],sizeof(int)*26);
fa[np]=fa[q]=nq;
while(to[p][cx]==q) to[p][cx]=nq,p=fa[p];
}
}
}
void dfs1(int x)
{
ss[x]=1;
vector<int>::iterator it;
for(it=e[x].begin();it!=e[x].end();++it)
{
int y=*it;
dfs1(y),sz[x]+=sz[y],ss[x]+=ss[y];
hs[x]=sz[hs[x]]>sz[y]?hs[x]:y;
}
}
void dfs2(int x,int ntp)
{
top[x]=ntp;
if(hs[x]) dfs2(hs[x],ntp);
vector<int>::iterator it;
for(it=e[x].begin();it!=e[x].end();++it)
{
int y=*it;
if(y==hs[x]) continue;
dfs2(y,y);
}
}
void inii()
{
for(int i=2;i<=tt;++i) e[fa[i]].push_back(i);
dfs1(1),dfs2(1,1);
}
void mark(int ii,int x)
{
while(x)
{
qs[x].push_back(ii);
x=fa[top[x]];
}
}
void wk1(int x)
{
if(px[x]) inst(rt[x]=++tot,0,px[x],px[x]);
vector<int>::iterator it;
for(it=e[x].begin();it!=e[x].end();++it)
{
int y=*it;
wk1(y);
rt[x]=merge(rt[x],rt[y]);
}
for(it=qs[x].begin();it!=qs[x].end();++it)
{
int i=*it,l=qr[i][0],r=qr[i][1];
an[i]=max(an[i],quer(rt[x],1,n,l,r-1,len[x]+l-1));
}
}
void wk2(int x)
{
int latp=tot;
vector<int>::iterator it;
for(it=e[x].begin();it!=e[x].end();++it)
{
int y=*it;
if(y==hs[x]) continue;
wk2(y);
}
for(it=op[x].begin();it!=op[x].end();++it)
{
int i=*it,las=rt[x];
inst(rt[x]=++tot,las,i,i-len[x]);
}
for(it=qs[x].begin();it!=qs[x].end();++it)
{
int i=*it,l=qr[i][0],r=qr[i][1];
an[i]=max(an[i],quer(rt[x],1,n,l,r-1,l-1));
}
if(hs[x]) rt[hs[x]]=rt[x],wk2(hs[x]);
if(x==top[x])
{
rt[x]=0;
while(tot>latp)
{
s[tot]=ch[tot][0]=ch[tot][1]=0;
--tot;
}
}
} int main()
{
//ntt
scanf("%s",cc+1);
n=strlen(cc+1);
for(int i=1;i<=n;++i) extd(cc[i]-'a',i);
inii();
q=rd();
for(int i=1;i<=q;++i)
{
qr[i][0]=rd(),qr[i][1]=rd();
an[i]=qr[i][0]-1;
mark(i,ps[qr[i][1]]);
}
s[0]=1<<30;
wk1(1);
while(tot)
{
s[tot]=ch[tot][0]=ch[tot][1]=0;
--tot;
}
for(int i=1;i<=n;++i)
{
int x=ps[i];
while(x)
{
op[x].push_back(i);
x=fa[top[x]];
}
}
for(int i=1;i<=tt;++i) rt[i]=0;
wk2(1);
for(int i=1;i<=q;++i) printf("%d\n",an[i]-qr[i][0]+1);
return 0;
}

luogu P4482 [BJWC2018]Border 的四种求法的更多相关文章

  1. luogu P4482 [BJWC2018] Border 的四种求法 - 后缀数组

    题目传送门 传送门 题目大意 区间border. 照着金策讲稿做. Code /** * luogu * Problem#P4482 * Accepted * Time: 8264ms * Memor ...

  2. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  3. [BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)

    题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border . Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+ ...

  4. [BJWC2018]Border 的四种求法

    description luogu 给一个小写字母字符串\(S\),\(q\)次询问每次给出\(l,r\),求\(s[l..r]\)的\(Border\). solution 我们考虑转化题面:给定\ ...

  5. 【LuoguP4482】[BJWC2018]Border 的四种求法

    题目链接 题意 区间 boder \(n,q\leq 2*10^5\) Sol (暴力哈希/SA可以水过) 字符串区间询问问题,考虑用 \(SAM\) 解决. boder相当于是询问区间 \([l,r ...

  6. 「BJWC2018」Border 的四种求法

    「BJWC2018」Border 的四种求法 题目描述 给一个小写字母字符串 \(S\) ,\(q\) 次询问每次给出 \(l,r\) ,求 \(s[l..r]\) 的 Border . \(1 \l ...

  7. 【洛谷4482】Border的四种求法(后缀自动机_线段树合并_链分治)

    这题我写了一天后交了一发就过了我好兴奋啊啊啊啊啊啊 题目 洛谷 4482 分析 这题明明可以在线做的,为什么我见到的所有题解都是离线啊 -- 什么时候有机会出一个在线版本坑人. 题目的要求可以转化为求 ...

  8. 四种浏览器对 clientHeight、offsetHeight、scrollHeight、clientWidth、offsetWidth 和 scrollWidth 的解释差异

    网页可见区域宽:document.body.clientWidth 网页可见区域高:document.body.clientHeight 网页可见区域宽:document.body.offsetWid ...

  9. 关于SWT/JFace的事件模型的四种方式

    事件的4种写法 1.匿名内部类方式的写法 2.命名内部类的写法 3.外部类写法 4.实现监听接口的写法 第一种用匿名内部类的方法: public class HelloWorld { private ...

随机推荐

  1. laravel 框架接入环信遇到的坑(-)

    在脚本中执行判断user表中是否注册环信时,报错: “请求错误:service_resource_not_found Service resource not found  ” // 判断环信是否已经 ...

  2. AsyncTaskMethodBuilder

    AsyncTaskMethodBuilder Represents a builder for asynchronous methods that return a task. public stru ...

  3. 在Excel中,已知身份证号码,如何批量计算其实际年龄?

    昨天,上司问我:..,你会在Excel中计算年龄吗?当时,一下促住了.说真的,还真不会.今天研究了一下,写下来,方便日后查看. 首先,得有一张已知姓名和相应身份证号的原表吧. 在这张表上再加上三列:出 ...

  4. SQLServer-设置-Table:阻止保存要求重新创建表的更改

    ylbtech-SQLServer-设置-Table:阻止保存要求重新创建表的更改 1.返回顶部 ·不允许保存更改,阻止保存要求重新创建表的更改 · 2.返回顶部 · https://jingyan. ...

  5. 代码实现:判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称

    package com.loaderman.test; import java.io.File; import java.io.FilenameFilter; public class Test { ...

  6. 五十一:数据库之Flask-Migrate详解

    在实际开发中,经常会发生数据库修改行为,一般数据库修改不是直接手动修改,而是去修改ORM模型,然后再把模型映射到数据库中,这些操作可以通过flask-migrate实现,flask-migrate是基 ...

  7. IPTV系统的VOD与TV业务性能测试

    IPTV的未来发展正在成为业界的焦点话题.据市场研究公司MRG的统计,全球IPTV用户将由2004年的200万增加至2010年的2000万,预计全球IPTV市场2005-2010年的复合增长率为102 ...

  8. openstack共享组件--rabbitmq消息队列(1)

    一.MQ 全称为 Message Queue, 消息队列( MQ ) 是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们. 消息传 ...

  9. Ubuntu 16.04安装anaconda3

    首先下载anaconda3镜像,清华大学开源软件镜像站下载地址:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 因为实验需要用到tenso ...

  10. 【并行计算-CUDA开发】从零开始学习OpenCL开发(一)架构

    多谢大家关注 转载本文请注明:http://blog.csdn.net/leonwei/article/details/8880012 本文将作为我<从零开始做OpenCL开发>系列文章的 ...