description
点击查看题目内容
solution
Step1
无脑建SAMSAMSAM
两个前缀的最长公共后缀就是parent−treeparent-treeparent−tree上两点的lcalcalca,定义知显然
Step2
离线询问,按右端点从小到大排序
Step3
每加入一个字母,就将tatata在parent−treeparent-treeparent−tree上到根节点的路径打上tatata的标记
如果遇到以前打的标记,则说明该节点即为旧标记与新标记的lcalcalca
贪心把标记覆盖为较大的值
Step4
树状数组统计
下标为左端点,每次查询下标大于等于该询问左端点的最大深度
向根跑的过程中每一次遇到旧标记,就在树状数组上更新答案,并给该节点打上新标记
但树状数组是用来计算前缀和的,所以有一个n−x+1n-x+1n−x+1的小转化
Step5
往根节点跑的过程即是LCTLCTLCT的accessaccessaccess操作
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;
}