Luogu P4036 火星人

题目大意

对于一个小写字母组成的字符串,有 (m) 种操作。

  1. ( exttt{L} ext{ }x ext{ }y) :询问分别从第 (x) 位、第 (y) 位开始的子串的最长公共前缀长度。
  2. ( exttt{R} ext{ }x ext{ }d) :将第 (x) 位的字符修改为 (d)
  3. ( exttt{I} ext{ }x ext{ }d) :在第 (x) 位后插入字符 (d)

其中,字符串长度 (L) 始终满足 (1 leq L leq 10^5) ,且字符串始终由小写字母组成, (1 leq n leq 1.5 imes 10^5) ,询问的操作个数不超过 (10^4) 个。

题解

容易想到,对于每一段子串可以用 Hash 进行比较。
([x,x+k))([y,y+k)) 相同,则 ([x,x+k-1))([y,y+k-1)) 一定相同。
([x,x+k))([y,y+k)) 不相同,则 ([x,x+k+1))([y,y+k+1)) 一定不相同。
显然我们可以用二分来做。
然而这一题还需要支持修改和插入操作,我们可以用平衡树来维护每一个点的 Hash 值。
设点 (i) 的 Hash 值为 (H(i)) ,左儿子为 (l_i) ,右儿子为 (r_i) ,子树大小为 (size(i)) ,对应的字母为 (val_i)
(H(i) = H(l_i) imes base^{size(r_i)+1} + val_i imes base^{size(r_i)} + H(r_i))
unsigned int + 自然溢出就好,取模太慢了。
代码自带大常数,开了 O2 才A。
听说 Splay 跑得比 FHQ Treap 快,但是不想打。。。

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <algorithm>

using std::swap;

#define MAX_N (100000 + 5)
#define MAX_M (150000 + 5)

struct FHQ_Treap
{
	int ch[2];
	int prior;
	unsigned val;
	unsigned hash;
	int size;
};

const unsigned base = 27;

int T;
char s[MAX_N];
int n, m;
int root, len;
FHQ_Treap t[MAX_N + MAX_M];
unsigned p[MAX_N + MAX_M];

int ReadInt()
{
	int res = 0;
	char ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) res = res * 10 + ch - '0', ch = getchar();
	return res;
}

char ReadChar()
{
	char ch = getchar();
	while (!isalpha(ch)) ch = getchar();
	return ch;
}

int newNode(int val)
{
	t[++len].prior = rand();
	t[len].ch[0] = t[len].ch[1] = 0;
	t[len].val = t[len].hash = val;
	t[len].size = 1;
	return len;
}

void Update(int idx)
{
	int l = t[idx].ch[0], r = t[idx].ch[1];
	t[idx].size = t[l].size + t[r].size + 1;
	t[idx].hash = t[l].hash * p[t[r].size + 1] + t[idx].val * p[t[r].size] + t[r].hash;
}

void Split(int idx, int rk, int & L, int & R)
{
	if (!idx)
	{
		L = R = 0;
		return;
	}
	if (t[t[idx].ch[0]].size + 1 <= rk)
	{
		L = idx;
		Split(t[idx].ch[1], rk - t[t[idx].ch[0]].size - 1, t[L].ch[1], R);
	}
	else
	{
		R = idx;
		Split(t[idx].ch[0], rk, L, t[R].ch[0]);
	}
	Update(idx);
}

int Merge(int L, int R)
{
	if (!L || !R) return L + R;
	if (t[L].prior < t[R].prior)
	{
		t[L].ch[1] = Merge(t[L].ch[1], R);
		Update(L);
		return L;
	}
	else
	{
		t[R].ch[0] = Merge(L, t[R].ch[0]);
		Update(R);
		return R;
	}
}	

unsigned Query(int l, int r)
{
	int L, M, R;
	unsigned res;
	Split(root, r, L, R);
	Split(L, l - 1, L, M);
	res = t[M].hash;
	root = Merge(Merge(L, M), R); 
	return res;
}

void Modify(int rk, unsigned val)
{
	int L, M, R;
	Split(root, rk - 1, L, R);
	Split(R, 1, M, R);
	t[M].val = t[M].hash = val;
	root = Merge(Merge(L, M), R);
}

void Insert(int rk, unsigned val)
{
	int L, R;
	Split(root, rk, L, R);
	root = Merge(Merge(L, newNode(val)), R);
}

int main()
{
	srand(19260817);
	p[0] = 1;
	for (int i = 1; i < MAX_N + MAX_M; ++i)
	{
		p[i] = p[i - 1] * base;
	}
	T = ReadInt();
	while (T--)
	{
		root = len = 0;
		scanf("%s", s + 1);
		m = ReadInt();
		n = strlen(s + 1);
		for (int i = 1; i <= n; ++i)
		{
			root = Merge(root, newNode(s[i] - 'a' + 1));
		}
		char ch, d;
		int x, y, l, r, mid, ans;
		while (m--)
		{
			ch = ReadChar();
			switch (ch)
			{
			case 'Q':
				x = ReadInt();
				y = ReadInt();
				if (x > y) swap(x, y);
				l = 0;
				r = len - y + 1;
				while (l < r)
				{
					mid = l + r + 1 >> 1;
					if (Query(x, x + mid - 1) != Query(y, y + mid - 1)) r = mid - 1;
					else l = mid;
				}
				printf("%d
", r);
				break;
			case 'R':
				x = ReadInt();
				d = ReadChar();
				Modify(x, d - 'a' + 1);
				break;		
			case 'I':
				x = ReadInt();
				d = ReadChar();
				Insert(x, d - 'a' + 1);
				break;
			}
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/kcn999/p/13473417.html