【loj6041】「雅礼集训 2017 Day7」事情的相似度 后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组

题目描述

给你一个长度为 $n$ 的01串,$m$ 次询问,每次询问给出 $l$ 、$r$ ,求从 $[l,r]$ 中选出两个不同的前缀的最长公共后缀长度的最大值。

$n,m\le 10^5$


题解

后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组

两个前缀的最长公共后缀,在正串后缀自动机上体现为pre树上两点LCA的深度。

考虑统计pre树上一个点的贡献:对于两个前缀 $x$ 、$y$ ,它能够影响的询问左端点小于等于 $x$ ,右端点大于等于 $y$ 。因此影响最大化的前缀对就是排序后相邻的两个,每次只需要考虑这些前缀对。

那么我们考虑两个子树合并的过程,使用STL-set维护前驱后继成为贡献。这个过程可以启发式合并,把小的合并到大的中。

剩下的就是对于询问 $[l,r]$ ,询问前缀对中第一个 $\ge l$ ,第二个 $\le r$ 的最大值。离线+扫描线+树状数组维护前缀最小值即可。

时间复杂度瓶颈在于启发式合并STL-set,为 $O(n\log^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\n" , ans[i]);
	return 0;
}

 

posted @ 2018-03-22 19:22  GXZlegend  阅读(832)  评论(0编辑  收藏  举报