description
solution
Step1
无脑建 S A M SAM SAM
两个前缀的最长公共后缀就是 p a r e n t − t r e e parent-tree parent−tree上两点的 l c a lca lca,定义知显然
Step2
离线询问,按右端点从小到大排序
Step3
每加入一个字母,就将 t a ta ta在 p a r e n t − t r e e parent-tree parent−tree上到根节点的路径打上 t a ta ta的标记
如果遇到以前打的标记,则说明该节点即为旧标记与新标记的 l c a lca lca
贪心把标记覆盖为较大的值
Step4
树状数组统计
下标为左端点,每次查询下标大于等于该询问左端点的最大深度
向根跑的过程中每一次遇到旧标记,就在树状数组上更新答案,并给该节点打上新标记
但树状数组是用来计算前缀和的,所以有一个 n − x + 1 n-x+1 n−x+1的小转化
Step5
往根节点跑的过程即是 L C T LCT LCT的 a c c e s s access access操作
code
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define maxn 200005
struct node {
int f, val, tag;
int son[2];
}tree[maxn];
struct SAM {
int fa, len;
int son[2];
}t[maxn];
vector < pair < int, int > > G[maxn];
int last = 1, cnt = 1, n, m;
char s[maxn];
int ans[maxn], maxx[maxn], num[maxn], st[maxn];
int lowbit( int x ) {
return x & ( -x );
}
void modify( int x, int val ) {
for( int i = n - x + 1;i <= n;i += lowbit( i ) )
maxx[i] = max( maxx[i], val );
}
int query( int x ) {
int ans = 0;
for( int i = n - x + 1;i;i -= lowbit( i ) )
ans = max( ans, maxx[i] );
return ans;
}
void insert( int id, int x ) {
int pre = last, now = last = ++ cnt;
num[id] = cnt;
t[now].len = t[pre].len + 1;
while( pre && ! t[pre].son[x] ) t[pre].son[x] = now, pre = t[pre].fa;
if( ! pre ) t[now].fa = 1;
else {
int u = t[pre].son[x];
if( t[pre].len + 1 == t[u].len ) t[now].fa = u;
else {
int v = ++ cnt;
t[v] = t[u];
t[v].len = t[pre].len + 1;
t[u].fa = t[now].fa = v;
while( pre && t[pre].son[x] == u ) t[pre].son[x] = v, pre = t[pre].fa;
}
}
}
bool isroot( int x ) {
return tree[tree[x].f].son[0] == x || tree[tree[x].f].son[1] == x;
}
void rotate( int x ) {
int fa = tree[x].f, Gfa = tree[fa].f;
int k = tree[fa].son[1] == x;
if( isroot( fa ) )
tree[Gfa].son[tree[Gfa].son[1] == fa] = x;
tree[x].f = Gfa;
if( tree[x].son[k ^ 1] )
tree[tree[x].son[k ^ 1]].f = fa;
tree[fa].son[k] = tree[x].son[k ^ 1];
tree[x].son[k ^ 1] = fa;
tree[fa].f = x;
}
void change( int x, int val ) {
tree[x].val = tree[x].tag = val;
}
void pushdown( int x ) {
if( ! tree[x].tag ) return;
if( tree[x].son[0] ) change( tree[x].son[0], tree[x].tag );
if( tree[x].son[1] ) change( tree[x].son[1], tree[x].tag );
tree[x].tag = 0;
}
void splay( int x ) {
int Top = 0, y = x;
st[++ Top] = y;
while( isroot( y ) ) st[++ Top] = y = tree[y].f;
while( Top ) pushdown( st[Top --] );
while( isroot( x ) ) {
int fa = tree[x].f, Gfa = tree[fa].f;
if( isroot( fa ) )
( ( tree[Gfa].son[0] == fa ) ^ ( tree[fa].son[0] == x ) ) ? rotate( x ) : rotate( fa );
rotate( x );
}
}
void access( int x, int val ) {
int son;
for( son = 0;x;son = x, x = tree[x].f ) {
splay( x );
modify( tree[x].val, t[x].len );
tree[x].son[1] = son;
}
tree[son].tag = tree[son].val = val;
}
int main() {
scanf( "%d %d %s", &n, &m, s + 1 );
for( int i = 1;i <= n;i ++ ) //树状数组下标必须从1开始
insert( i, s[i] - '0' );
for( int i = 1, l, r;i <= m;i ++ ) {
scanf( "%d %d", &l, &r );
G[r].push_back( make_pair( l, i ) );
}
for( int i = 1;i <= cnt;i ++ ) tree[i].f = t[i].fa;
for( int i = 1;i <= n;i ++ ) {
access( num[i], i );
for( int j = 0;j < G[i].size();j ++ )
ans[G[i][j].second] = query( G[i][j].first );
}
for( int i = 1;i <= m;i ++ )
printf( "%d\n", ans[i] );
return 0;
}