「BZOJ3946」無聊的遊戲-線段樹+哈希

Description

你有nn個串,有兩種操作

  • [l,r][l,r]的串在前面拼上新串ss
  • 詢問lcp(sl,sl+1,sl+2,...,sr)lcp(s_l,s_{l+1},s_{l+2},...,s_{r})

n50000,S600000n\leq 50000, \sum |S| \leq 600000

Solution

考慮把所有添加串+初始串拼成一個大串SS。那麼所有串顯然是這個大串的若干個不相交的子串拼起來的。

考慮用線段樹維護每個串。如果某個串是由S[l1,r1],S[l2,r2],...,S[lk,rk]S[l_1,r_1],S[l_2,r_2],...,S[l_k,r_k]拼成的,那麼在它的線段樹對區間[l1,r1],[l2,r2],...,[lk,rk][l_1,r_1],[l_2,r_2],...,[l_k,r_k]執行+1+1操作。由於每次操作是對一段區間的串添加上一個子串,所以考慮用線段樹套線段樹維護所有的串。如果對一段區間[l,r][l,r]的串拼上一個串S[L,R]S[L,R],那麼在第一維線段樹上的[l,r[l,r區間打上標記,這個標記是一棵在[L,R][L,R]+1+1的第二維線段樹。下傳標記就是線段樹合併。由於範圍不交,所以複雜度是O(nlognlogL)O(nlognlogL)LLSS的長度)。

考慮詢問,由於lcp(sl,sl+1,sl+2,...,sr)=min{lcp(sl,sl+1),lcp(sl+1,sl+2),...,lcp(sr1,sr)}lcp(s_l,s_{l+1},s_{l+2},...,s_{r})=min\{ lcp(s_l,s_{l+1}),lcp(s_{l+1},s_{l+2}),...,lcp(s_{r-1},s_r)\}。所以維護相鄰兩個串的lcplcp即可。考慮修改對lcplcp的影響,顯然只會影響lcp(l1,l)lcp(l-1,l)lcp(r,r+1)lcp(r,r+1),對於i[l+1,r1]lcp(i,i+1)\forall i \in [l+1,r-1] lcp(i,i+1)加上添加串的長度即可。

#include <bits/stdc++.h>
using namespace std;

inline int gi()
{
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	int sum = 0;
	while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

typedef unsigned int uint;
const int maxn = 50005, maxs = 600005;

int n, m;
int S[maxs], cnt, st[maxn], en[maxn], stq[maxn], enq[maxn];
int q[maxn][3];
char s[maxs];
uint Pow[maxs];

int rt[maxn]; //每個串在sgt1上的根

#define mid ((l + r) >> 1)

namespace sgt1
{

	const int maxn = maxs * 60;

	int tot, lch[maxn], rch[maxn], sum[maxn];
	uint h[maxn];

	void update(int s)
	{
		sum[s] = sum[lch[s]] + sum[rch[s]];
		h[s] = h[lch[s]] * Pow[sum[rch[s]]] + h[rch[s]];
	}

	void build(int &s, int l, int r, int ql, int qr)
	{
		s = ++tot;
		if (l == r) return sum[s] = 1, h[s] = S[l], void();
		if (ql <= mid) build(lch[s], l, mid, ql, qr);
		if (qr >= mid + 1) build(rch[s], mid + 1, r, ql, qr);
		update(s);
	}

	int merge(int s1, int s2)
	{
		if (!s1 || !s2) return s1 | s2;
		int s = ++tot;
		lch[s] = merge(lch[s1], lch[s2]);
		rch[s] = merge(rch[s1], rch[s2]);
		return update(s), s;
	}

	uint hash(int s, int l, int r, int k)
	{
		if (l == r) return k ? h[s] : 0;
		if (sum[lch[s]] >= k) return hash(lch[s], l, mid, k);
		else return k -= sum[lch[s]], h[lch[s]] * Pow[k] + hash(rch[s], mid + 1, r, k);
	}

}

#define	lch (s << 1)
#define rch (s << 1 | 1)

namespace sgt2
{
	
	int tag[maxs << 2];

	inline void pushdown(int s)
	{
		if (!tag[s]) return ;
		tag[lch] = sgt1::merge(tag[lch], tag[s]);
		tag[rch] = sgt1::merge(tag[rch], tag[s]);
		tag[s] = 0;
	}

	void modify(int s, int l, int r, int ql, int qr, int x)
	{
		if (ql <= l && r <= qr) return tag[s] = sgt1::merge(tag[s], x), void();
		pushdown(s);
		if (ql <= mid) modify(lch, l, mid, ql, qr, x);
		if (qr >= mid + 1) modify(rch, mid + 1, r, ql, qr, x);
	}

	void query(int s, int l, int r, int p)
	{
		if (l == r) return rt[p] = sgt1::merge(rt[p], tag[s]), tag[s] = 0, void();
		pushdown(s);
		if (p <= mid) query(lch, l, mid, p);
		else query(rch, mid + 1, r, p);
	}

	int lcp(int x, int y)
	{
		query(1, 1, n, x); query(1, 1, n, y);
		x = rt[x]; y = rt[y];
		int l = 0, r = min(sgt1::sum[x], sgt1::sum[y]), Mid;
		while (l < r) {
			Mid = (l + r + 1) >> 1;
			if (sgt1::hash(x, 1, cnt, Mid) != sgt1::hash(y, 1, cnt, Mid))  r = Mid - 1;
			else l = Mid;
		}
		return l;
	}

}

namespace sgt3
{
	
	int h[maxn << 2], tag[maxn << 2];

	inline void pushdown(int s)
	{
		if (!tag[s]) return ;
		h[lch] += tag[s]; tag[lch] += tag[s];
		h[rch] += tag[s]; tag[rch] += tag[s];
		tag[s] = 0;
	}

	inline void update(int s) {h[s] = min(h[lch], h[rch]);}

	void build(int s, int l, int r)
	{
		if (l == r) return h[s] = sgt2::lcp(l, l + 1), void();
		build(lch, l, mid);
		build(rch, mid + 1, r);
		update(s);
	}

	void modify(int s, int l, int r, int p)
	{
		if (l == r) return h[s] = sgt2::lcp(l, l + 1), void();
		pushdown(s);
		if (p <= mid) modify(lch, l, mid, p);
		else modify(rch, mid + 1, r, p);
		update(s);
	}

	void modify(int s, int l, int r, int ql, int qr, int v)
	{
		if (ql <= l && r <= qr) return h[s] += v, tag[s] += v, void();
		pushdown(s);
		if (ql <= mid) modify(lch, l, mid, ql, qr, v);
		if (qr >= mid + 1) modify(rch, mid + 1, r, ql, qr, v);
		update(s);
	}

	int query(int s, int l, int r, int ql, int qr)
	{
		if (ql <= l && r <= qr) return h[s];
		pushdown(s);
		int res = 1e9;
		if (ql <= mid) res = query(lch, l, mid, ql, qr);
		if (qr >= mid + 1) res = min(res, query(rch, mid + 1, r, ql, qr));
		return res;
	}

}

int main()
{
	freopen("uzi.in", "r", stdin);
	freopen("uzi.out", "w", stdout);

	n = gi(); m = gi();
	static int tmp[maxs];
	for (int len, i = 1; i <= n; ++i) {
		scanf("%s", s + 1); len = strlen(s + 1);
		st[i] = cnt + 1;
		for (int j = 1; j <= len; ++j) tmp[++cnt] = s[j] - 'a';
		en[i] = cnt;
	}

	char c;
	for (int len, i = 1; i <= m; ++i) {
		c = getchar();
		while (c < 'A' || c > 'Z') c = getchar();
		q[i][1] = gi(); q[i][2] = gi();
		if (c == 'Q') q[i][0] = 1;
		else {
			scanf("%s", s + 1); len = strlen(s + 1);
			stq[i] = cnt + 1;
			for (int j = 1; j <= len; ++j) tmp[++cnt] = s[j] - 'a';
			enq[i] = cnt;
		}
	}

	cnt = 0;
	for (int i = m, l, r; i >= 1; --i)
		if (!q[i][0]) {
			l = stq[i]; r = enq[i];
			stq[i] = cnt + 1;
			for (int j = l; j <= r; ++j) S[++cnt] = tmp[j];
			enq[i] = cnt;
		}
	
	for (int i = 1, l, r; i <= n; ++i) {
		l = st[i]; r = en[i];
		st[i] = cnt + 1;
		for (int j = l; j <= r; ++j) S[++cnt] = tmp[j];
		en[i] = cnt;
	}
	
	Pow[0] = 1;
	for (int i = 1; i <= cnt; ++i) Pow[i] = Pow[i - 1] * 141937;
	for (int i = 1; i <= n; ++i) sgt1::build(rt[i], 1, cnt, st[i], en[i]);
	if (n > 1) sgt3::build(1, 1, n - 1);

	for (int l, r, i = 1; l = q[i][1], r = q[i][2], i <= m; ++i)
		if (q[i][0]) {
			if (l == r) sgt2::query(1, 1, n, l), printf("%d\n", sgt1::sum[rt[l]]);
			else printf("%d\n", sgt3::query(1, 1, n - 1, l, r - 1));
		} else {
			int tag;
			sgt1::build(tag, 1, cnt, stq[i], enq[i]);
			sgt2::modify(1, 1, n, l, r, tag);
			if (l < r) sgt3::modify(1, 1, n - 1, l, r - 1, enq[i] - stq[i] + 1);
			if (l > 1) sgt3::modify(1, 1, n - 1, l - 1);
			if (r < n) sgt3::modify(1, 1, n - 1, r);
		}

	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章