• P4036 [JSOI2008]火星人


    题意描述

    洛谷

    给你一个字符串,让你支持三个操作。

    • 操作一:查询当前字符串以 (x) 为开头的后缀和以 (y) 为开头的后缀的 (lcp)
    • 操作二:将当前字符串的第 (x) 个字符改为 (ch)
    • 操作三:在当前字符串的第 (x) 个字符后面添加一个字符 (c)

    数据范围: 操作数 (mleq 1.5 imes 10^5) , 字符串长度 (nleq 10^5)

    solution:

    平衡树加哈希。

    求两个字符串的 (lcp) 无非就两种方法:后缀数组和哈希。

    如果说我们没有第二个和第三个操作的话,可以直接用后缀数组求解。

    但维护操作二和操作三的话需要可持久化后缀数组(好像没有这个玩意)。

    那我们只能用哈希来求两个字符串的 (lcp)

    我们又要动态维护这个字符串,可以考虑用平衡树维护一下哈希。

    具体操作如下:

    对于每个·节点 (i),维护一个 (sum[i]) 数组表示中序遍历这个节点子树所形成的字符串 (s[i]) 的哈希值。

    同时维护两个数组 (siz[i])(ch[i]) 表示子树的大小,和 (i) 号点所代表的的字符。

    对于字符串 (s[i]) 显然是由 (s[son[i][0]] + ch[i] + s[son[i][1]]) 拼接起来的。

    根据字符串哈希那套理论可得:

    (large sum[i] = sum[son[i][0]] imes p^{siz[son[i][1]]+1} + ch[i] imes p^{siz[son[i][1]]} + sum[son[i][1]])

    求当前字符串的第 (l) 个字符到第 (r) 个字符所组成的字符串的哈希值时,设 (x = rk(l-1), y = rk(r+1))

    把 节点 (x) 旋转到根, (y) 旋转到 (x) 的儿子上,(sum[y][0]) 即为所求。

    操作二和操作三就是平衡树的插入和修改操作,随便维护一下就行。

    最后注意写的时候要注意各种小细节,不然又需要调很长时间。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define ull unsigned long long
    const int p = 13131;
    const int N = 2e5+10;
    int n,m,tot,root,x,y;
    int siz[N],fa[N],son[N][2];
    ull sum[N],val[N],w[N],base[N];
    char ch,opt,a[N];
    inline int read()
    {
        int s = 0, w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    void up(int o)
    {
    	siz[o] = siz[son[o][0]] + siz[son[o][1]] + 1;
    	sum[o] = sum[son[o][0]] * base[siz[son[o][1]]+1] + val[o] * base[siz[son[o][1]]] + sum[son[o][1]];
    }
    bool ispant(int x)
    {
    	return son[fa[x]][0] == x ? 0 : 1;
    }
    void build(int &o,int f,int l,int r)
    {
    	if(l > r) return;
    	o = ++tot;
    	int mid = (l+r)>>1;
    	sum[o] = val[o] = w[mid]; siz[o] = 1; fa[o] = f;
    	build(son[o][0],o,l,mid-1);
    	build(son[o][1],o,mid+1,r);
    	up(o);
    }
    void retate(int x)
    {
    	int y = fa[x], z = fa[y];
    	int px = ispant(x), py = ispant(y);
    	son[y][px] = son[x][px^1];
    	if(son[x][px^1]) fa[son[x][px^1]] = y;
    	son[x][px^1] = y;
    	fa[y] = x;
    	if(z) son[z][py] = x;
    	fa[x] = z;
    	up(y); up(x);
    }
    void splay(int x,int to)
    {
    	while(fa[x] != to)
    	{
    		int y = fa[x], z = fa[y];
    		int px = ispant(x), py = ispant(y);
    		if(z == to) retate(x);
    		else
    		{
    			px == py ? retate(y) : retate(x);
    			retate(x);
    		}
    		up(y); up(x);
    	}
    	if(to == 0) root = x;
    }
    int kth(int k)
    {
    	int o = root;
    	while(1)
    	{
    		if(siz[son[o][0]] >= k) o = son[o][0];
    		else if(siz[son[o][0]] + 1 == k) return o;
    		else k -= siz[son[o][0]] + 1, o = son[o][1];
    	}
    }
    ull get(int l,int r)//求当前字符串的第l个字符到第r个字符组成的字符串的哈希值
    {
    	int x = kth(l), y = kth(r+2);
    	splay(x,0); splay(y,x);
    	return sum[son[y][0]];
    }
    int lcp(int x,int y)//二分加hash求lcp
    {
    	int L = 1, R = min(n-x+1,n-y+1), ans = 0;
    	while(L <= R)
    	{
    		int mid = (L + R)>>1;
    		if(get(x,x+mid-1) == get(y,y+mid-1))
    		{
    			ans = mid;
    			L = mid + 1;
    		}
    		else R = mid - 1;
    	}
    	return ans;
    }
    int main()
    {
    	scanf("%s",a+1);
    	n = strlen(a+1); base[0] = 1;
    	for(int i = 1; i <= 2*n; i++) base[i] = base[i-1] * p;
    	w[0] = 0, w[n+1] = 0;
    	for(int i = 1; i <= n; i++) w[i] = a[i] - 'a';
    	build(root,0,0,n+1);
    	m = read();
    	for(int i = 1; i <= m; i++)
    	{
    		cin>>opt;
    		if(opt == 'Q')
    		{
    			x = read(); y = read();
    			printf("%d
    ",lcp(x,y));
    		}
    		if(opt == 'R')
    		{
    			x = read(); cin>>ch;
    			int l = kth(x), r = kth(x+2);//单点修改
    			splay(l,0); splay(r,l);
    			sum[son[r][0]] = val[son[r][0]] = ch - 'a';
    			up(r); up(l);
    		}
    		if(opt == 'I')
    		{
    			x = read(); cin>>ch;
    			int o = ++tot; n++;
    			sum[o] = val[o] = ch - 'a'; siz[o] = 1;//插入一个数
    			int l = kth(x+1), r = kth(x+2);
    			splay(l,0); splay(r,l);
    			son[r][0] = o; fa[o] = r;
    			while(fa[o]) o = fa[o], up(o);
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Windows Phone 7 立体旋转动画的实现
    jQuery 表格Table插件汇总
    SNS社交类网站照片头像裁剪源码
    VS无法启动调试
    SQL Server中获取第一天、最后一天
    jQuery技巧总结
    IT人士应当知道的10个行业小内幕
    巧用SQL server临时表
    将Html文档整理为规范XML文档
    16个Javascript的Web UI库、框架及工具包
  • 原文地址:https://www.cnblogs.com/genshy/p/14413430.html
Copyright © 2020-2023  润新知