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

    题目描述

    给你一个长度为 $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;
    }
    
  • 相关阅读:
    柔性的沟通
    as3简单的文字提示队列
    恢复低版本的FlashPlayer
    AIR切换SDK版本时遇到的问题
    文字闪烁提示
    as3 ISOLIB学习资料整理
    xml文件转二进制文件的AIR
    Photoshop CS5的序列号
    MySQL导出、导出数据
    as3 shake动画
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8625876.html
Copyright © 2011-2022 走看看