给你一个长度为 $n$ 的01串,$m$ 次询问,每次询问给出 $l$ 、$r$ ,求从 $[l,r]$ 中选出两个不同的前缀的最长公共后缀长度的最大值。
$n,mle 10^5$
题解
后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组
两个前缀的最长公共后缀,在正串后缀自动机上体现为pre树上两点LCA的深度。
考虑统计pre树上一个点的贡献:对于两个前缀 $x$ 、$y$ ,它能够影响的询问左端点小于等于 $x$ ,右端点大于等于 $y$ 。因此影响最大化的前缀对就是排序后相邻的两个,每次只需要考虑这些前缀对。
那么我们考虑两个子树合并的过程,使用STL-set维护前驱后继成为贡献。这个过程可以启发式合并,把小的合并到大的中。
剩下的就是对于询问 $[l,r]$ ,询问前缀对中第一个 $ge l$ ,第二个 $le r$ 的最大值。离线+扫描线+树状数组维护前缀最小值即可。
时间复杂度瓶颈在于启发式合并STL-set,为 $O(nlog^2n)$ 。
#include <set> #include <cstdio> #include <cstring> #include <algorithm> #define N 200010 using namespace std; struct data { int x , y , z; data() {} data(int a , int b , int c) {x = a , y = b , z = c;} bool operator<(const data &a)const {return x < a.x;} }a[N * 20] , q[N]; set<int> s[N]; int pre[N] , c[N][2] , dis[N] , val[N] , last = 1 , tot = 1 , head[N] , to[N] , next[N] , cnt , bl[N] , ta , f[N] , n , ans[N]; char str[N]; void insert(int k , int ch) { int p = last , np = last = ++tot; dis[np] = dis[p] + 1 , s[np].insert(k); while(p && !c[p][ch]) c[p][ch] = np , p = pre[p]; if(!p) pre[np] = 1; else { int q = c[p][ch]; if(dis[q] == dis[p] + 1) pre[np] = q; else { int nq = ++tot; memcpy(c[nq] , c[q] , sizeof(c[q])); dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , pre[np] = pre[q] = nq; while(p && c[p][ch] == q) c[p][ch] = nq , p = pre[p]; } } } inline void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } void dfs(int x) { int i; set<int>::iterator j , k; for(i = head[x] ; i ; i = next[i]) { dfs(to[i]); if(s[bl[x]].size() > s[bl[to[i]]].size()) { for(j = s[bl[to[i]]].begin() ; j != s[bl[to[i]]].end() ; j ++ ) { k = s[bl[x]].upper_bound(*j); if(k != s[bl[x]].end()) a[++ta] = data(*j , *k , dis[x]); if(k != s[bl[x]].begin()) a[++ta] = data(*--k , *j , dis[x]); s[bl[x]].insert(*j); } } else { for(j = s[bl[x]].begin() ; j != s[bl[x]].end() ; j ++ ) { k = s[bl[to[i]]].upper_bound(*j); if(k != s[bl[to[i]]].end()) a[++ta] = data(*j , *k , dis[x]); if(k != s[bl[to[i]]].begin()) a[++ta] = data(*--k , *j , dis[x]); s[bl[to[i]]].insert(*j); } bl[x] = bl[to[i]]; } } } inline void fix(int x , int a) { int i; for(i = x ; i <= n ; i += i & -i) f[i] = max(f[i] , a); } inline int query(int x) { int i , ans = 0; for(i = x ; i ; i -= i & -i) ans = max(ans , f[i]); return ans; } int main() { int m , i , p; scanf("%d%d%s" , &n , &m , str + 1); for(i = 1 ; i <= n ; i ++ ) insert(i , str[i] - '0'); for(i = 2 ; i <= tot ; i ++ ) add(pre[i] , i); for(i = 1 ; i <= tot ; i ++ ) bl[i] = i; dfs(1); for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &q[i].x , &q[i].y) , q[i].z = i; sort(a + 1 , a + ta + 1) , sort(q + 1 , q + m + 1); for(p = ta , i = m ; i ; i -- ) { while(p && a[p].x >= q[i].x) fix(a[p].y , a[p].z) , p -- ; ans[q[i].z] = query(q[i].y); } for(i = 1 ; i <= m ; i ++ ) printf("%d " , ans[i]); return 0; }