Codeforces

思路

感觉这个离线的思路好神仙啊qwq

对于每个询问\([l,r]\)其实就是要求\(p_{max}\),使得\(lcs(s[1,p],s[1,r])>p-l\),也就是\(lcs(s[1,p],s[1,r])+l>p\)。

首先把询问离线按\(r\)排序,然后从右往左扫,每次

  1. 处理之前已经被加进去的询问,看当前位置是否能被作为\(p\),然后把已经处理完毕的询问给删掉。
  2. 把当前询问塞进去。

建出反串的后缀树,那么不等式左边就是\(dep_{lca(p,r)}+l\)。

暴力的做法就是枚举\(lca\),然后处理询问的时候查一下这个位置的\(l\),塞询问的时候把\(l\)塞进去。

用树链剖分加速:对于每一条链把\(tag\)打在深度最深的地方。处理询问的时候就查一下\(p\)到根上的\(\max(dep_x+l)\)以及这个\(tag\)来自哪个询问,然后将它删掉。塞询问的时候就一路往上跳,打\(tag\)。

怎么查询?分最大的\(tag\)是在上面还是下面来讨论,如果是上面就直接查,否则查一下\(\max(l)\)再加上当前的\(dep\)。

由于每个点只能有一个最大的\(tag\)放在线段树上,所以还要用一个\(set\)记录这个点有哪些标记。

(可能讲得不是很清楚qwq)

代码

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define drep(i,x,y) for (int i=(x);i>=(y);i--)
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define templ template<typename T>
#define sz 402020
typedef long long ll;
typedef double db;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
templ inline void read(T& t)
{
t=0;char f=0,ch=getchar();double d=0.1;
while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
t=(f?-t:t);
}
template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
char __sr[1<<21],__z[20];int __C=-1,__zz=0;
inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
inline void print(register int x)
{
if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
while(__z[++__zz]=x%10+48,x/=10);
while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
}
void file()
{
#ifdef NTFOrz
freopen("a.in","r",stdin);
#endif
}
inline void chktime()
{
#ifndef ONLINE_JUDGE
cout<<(clock()-t)/1000.0<<'\n';
#endif
}
#ifdef mod
ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
ll inv(ll x){return ksm(x,mod-2);}
#else
ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
#endif
// inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std; int n,Q;
char s[sz];
int pos[sz];
struct hh{int l,r,id;const bool operator < (const hh &x) const {return r>x.r;}}q[sz];
int ans[sz]; int TT;
//struct SegmentTree
//{
pii tr[sz<<2][2];
#define ls k<<1
#define rs k<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
void pushup(int k){tr[k][TT]=max(tr[ls][TT],tr[rs][TT]);}
void modify(int k,int l,int r,int x,pii w)
{
if (l==r) return (void)(tr[k][TT]=w);
int mid=(l+r)>>1;
if (x<=mid) modify(lson,x,w);
else modify(rson,x,w);
pushup(k);
}
pii query(int k,int l,int r,int x,int y)
{
if (x<=l&&r<=y) return tr[k][TT];
int mid=(l+r)>>1;pii ret=MP(-1e9,0);
if (x<=mid) chkmax(ret,query(lson,x,y));
if (y>mid) chkmax(ret,query(rson,x,y));
return ret;
}
//}T1,T2;
// 1:维护一段里面最大的值是多少,以及它的位置。
// 2:维护一段里面加的l的最大值是多少,以及它的位置。 namespace Tree
{
struct hh{int t,nxt;}edge[sz];
int head[sz],ecnt;
void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;}
int dfn[sz],top[sz],bot[sz],size[sz],son[sz],fa[sz],len[sz],T;
#define v edge[i].t
void dfs1(int x)
{
size[x]=1;
go(x)
{
fa[v]=x;dfs1(v);
size[x]+=size[v];
if (size[v]>size[son[x]]) son[x]=v;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++T;
top[x]=tp,bot[tp]=x;
if (son[x]) dfs2(son[x],tp);
go(x) if (v!=son[x]) dfs2(v,v);
}
#undef v
set<pii>s[sz];
// 维护这个地方打的标记有哪些
pii query(int x)
{
pii ret=MP(0,0);
while (x)
{
TT=0;pii w1=::query(1,1,T,dfn[top[x]],dfn[x]);
TT=1;pii w2=::query(1,1,T,dfn[x],dfn[bot[top[x]]]);
w2.fir+=len[x];
chkmax(ret,w1),chkmax(ret,w2);
x=fa[top[x]];
}
return ret;
}
void modify(int x,int w,int id,bool add)
{
while (x)
{
if (add) s[x].insert(MP(w,id));
else s[x].erase(MP(w,id));
if (s[x].size())
{
auto cur=*s[x].rbegin();
TT=1;::modify(1,1,T,dfn[x],cur);
cur.fir+=len[x];
TT=0;::modify(1,1,T,dfn[x],cur);
}
else TT=0,::modify(1,1,T,dfn[x],MP(0,0)),TT=1,::modify(1,1,T,dfn[x],MP(0,0));
x=fa[top[x]];
}
}
} namespace SAM
{
struct hh{int link,len,ch[28];}a[sz];
int lst=1,cnt=1,root=1;
int add(int c)
{
int cur=++cnt,p=lst;lst=cur;a[cur].len=a[p].len+1;
while (p&&!a[p].ch[c]) a[p].ch[c]=cur,p=a[p].link;
if (!p) return a[cur].link=1,cur;
int q=a[p].ch[c];
if (a[q].len==a[p].len+1) return a[cur].link=q,cur;
int t=++cnt;a[t]=a[q];a[q].link=a[cur].link=t;a[t].len=a[p].len+1;
while (p&&a[p].ch[c]==q) a[p].ch[c]=t,p=a[p].link;
return cur;
}
void build(){rep(i,2,cnt) Tree::make_edge(a[i].link,i),Tree::len[i]=a[i].len;}
} int main()
{
file();
read(n,Q);
cin>>(s+1);
rep(i,1,n) pos[i]=SAM::add(s[i]-'a'+1);
SAM::build();
Tree::dfs1(1),Tree::dfs2(1,1);
rep(i,1,Q) read(q[i].l,q[i].r),q[i].id=i;
sort(q+1,q+Q+1);
int p=1;
drep(i,n,1)
{
while (233)
{
pii w=Tree::query(pos[i]);int id=w.sec;
if (w.fir<=i) break;
ans[q[id].id]=i-q[id].l+1;
Tree::modify(pos[q[id].r],q[id].l,id,0);
}
while (p<=Q&&q[p].r==i) Tree::modify(pos[q[p].r],q[p].l,p,1),++p;
}
rep(i,1,Q) printf("%d\n",ans[i]);
return 0;
}

CF gym 100962D Deep Purple [后缀树,树链剖分,线段树]的更多相关文章

  1. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  2. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  3. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  4. 2019西北工业大学程序设计创新实践基地春季选拔赛 I Chino with Rewrite (并查集+树链剖分+线段树)

    链接:https://ac.nowcoder.com/acm/contest/553/I 思路:离线整棵树,用并查集维护下联通的情况,因为值只有60个,用2的x(1<=x<=60)次方表示 ...

  5. bzoj 4196 [Noi2015]软件包管理器 (树链剖分+线段树)

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2852  Solved: 1668[Submit][Sta ...

  6. ACM-ICPC 2018 焦作赛区网络预赛 E Jiu Yuan Wants to Eat (树链剖分+线段树)

    题目链接:https://nanti.jisuanke.com/t/31714 题意:给你一棵树,初始全为0,有四种操作: 1.u-v乘x    2.u-v加x   3. u-v取反  4.询问u-v ...

  7. 51Nod 1199 Money out of Thin Air (树链剖分+线段树)

    1199 Money out of Thin Air  题目来源: Ural 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题  收藏  关注 一棵有N个节点的树,每 ...

  8. fzu 2082 过路费 (树链剖分+线段树 边权)

    Problem 2082 过路费 Accept: 887    Submit: 2881Time Limit: 1000 mSec    Memory Limit : 32768 KB  Proble ...

  9. 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp

    题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...

随机推荐

  1. 解决Ubuntu 16.04 环境下Python 无法显示中文的问题

    一.下载中文字体(https://pan.baidu.com/s/1EqabwENMxR2WJrHfKvyrIw 这里下载多是SImhei字体) 安装字体:解压:unzip SimHei.zip拷贝字 ...

  2. vs code 调试设置

    首先vs code 安装插件:Debugger for Chrome vscode 设置:点击调试按钮,然后调试面板界面再点击设置按钮,添加一个配置,选择环境为:chrome编辑器自动生成一个laun ...

  3. Ane技术大全 - Devil程序员

    来源:http://www.th7.cn/Program/Android/201405/206863.shtml 一.Ane概况 1.Ane是什么? Ane是为了解决 as3(flash)与Java代 ...

  4. 使nginx支持pathinfo模式

    在将fastadmin部署到虚拟机中时,遇到如下问题:当访问登录页面时,页面进行不断的循环跳转重定向.解决方法是将nginx配置为支持pathinfo的模式 以下是nginx中的配置内容: locat ...

  5. AutoMapper 初次使用心得

    本例以asp.net webform为例: 结构: 主要代码:AutoMapperConfig 类 public class AutoMapperConfig { public static void ...

  6. Linux ping:unknown host问题排查

    一.检查网卡配置:输入ifconfig可以查看当前网卡配置的IP地址并且查看配置文件中网络的设置: [root@bqh- ~]# ifconfig eth0 Link encap:Ethernet H ...

  7. mysql遇到时区问题的坑(Java解决方案)

    最近项目遇到一个坑,就是server和db之间存在时区问题,本人的db是utc时间, 可以使用代码设置时区来解决,本人这里使用joda三方包,joda蛮好用的,具体用法这里不做详细描述. 先引入pom ...

  8. 阿里云ECS部署maven项目

    1.配置JDK和tomcat: https://www.cnblogs.com/congcongdi/p/11227771.html 2.使用eclipse打包项目 在项目上点击右键-->run ...

  9. SELinux 权限设置

    SELinux 权限设置 一.SELinux简介 SELinux全称是Security Enhanced Linux,由美国国家安全部(National Security Agency)领导开发的GP ...

  10. python获取第前多少天的日期

    1. 显示昨天(前一天)的日期 from datetime import date, timedelta yesterday_date = (date.today() + timedelta(days ...