「BZOJ3946」无聊的游戏-线段树+哈希

Description

你有 n n n个串,有两种操作

  • [ l , r ] [l,r] [l,r]的串在前面拼上新串 s s s
  • 询问 l c p ( s l , s l + 1 , s l + 2 , . . . , s r ) lcp(s_l,s_{l+1},s_{l+2},...,s_{r}) lcp(sl,sl+1,sl+2,...,sr)

n ≤ 50000 , ∑ ∣ S ∣ ≤ 600000 n\leq 50000, \sum |S| \leq 600000 n50000,S600000

Solution

考虑把所有添加串+初始串拼成一个大串 S S S。那么所有串显然是这个大串的若干个不相交的子串拼起来的。

考虑用线段树维护每个串。如果某个串是由 S [ l 1 , r 1 ] , S [ l 2 , r 2 ] , . . . , S [ l k , r k ] S[l_1,r_1],S[l_2,r_2],...,S[l_k,r_k] S[l1,r1],S[l2,r2],...,S[lk,rk]拼成的,那么在它的线段树对区间 [ l 1 , r 1 ] , [ l 2 , r 2 ] , . . . , [ l k , r k ] [l_1,r_1],[l_2,r_2],...,[l_k,r_k] [l1,r1],[l2,r2],...,[lk,rk]执行 + 1 +1 +1操作。由于每次操作是对一段区间的串添加上一个子串,所以考虑用线段树套线段树维护所有的串。如果对一段区间 [ l , r ] [l,r] [l,r]的串拼上一个串 S [ L , R ] S[L,R] S[L,R],那么在第一维线段树上的 [ l , r [l,r [l,r区间打上标记,这个标记是一棵在 [ L , R ] [L,R] [L,R] + 1 +1 +1的第二维线段树。下传标记就是线段树合并。由于范围不交,所以复杂度是 O ( n l o g n l o g L ) O(nlognlogL) O(nlognlogL) L L L S S S的长度)。

考虑询问,由于 l c p ( s l , s l + 1 , s l + 2 , . . . , s r ) = m i n { l c p ( s l , s l + 1 ) , l c p ( s l + 1 , s l + 2 ) , . . . , l c p ( s r − 1 , s r ) } 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)\} lcp(sl,sl+1,sl+2,...,sr)=min{lcp(sl,sl+1),lcp(sl+1,sl+2),...,lcp(sr1,sr)}。所以维护相邻两个串的 l c p lcp lcp即可。考虑修改对 l c p lcp lcp的影响,显然只会影响 l c p ( l − 1 , l ) lcp(l-1,l) lcp(l1,l) l c p ( r , r + 1 ) lcp(r,r+1) lcp(r,r+1),对于 ∀ i ∈ [ l + 1 , r − 1 ] l c p ( i , i + 1 ) \forall i \in [l+1,r-1] lcp(i,i+1) i[l+1,r1]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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值