bzoj2555 SubString (SAM+LCT维护子树大小/ETT)

Description

懒得写背景了,给你一个字符串init,要求你支持两个操作
(1):在当前字符串的后面插入一个字符串
(2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
你必须在线支持这些操作。

毒瘤

显然就是sam求endpos大小,强套LCT。
时隔多年又打了一次维护子树大小。
大致就是维护:
fsum[x]表示虚父亲为x的tsum和。
tsum[x]表示x的splay和,=tsum[child] + fsum[x] + v[x]

总共有几个细节需要注意

  1. access时相当于实化一条虚边,添加一条虚边。要修改贡献,不必考虑对更上方的影响因为会access到。
  2. link时要添加虚边,此时假若是从fa[a]=b,则需要对b修改贡献。将b access并splay,就不用考虑对上方的贡献了。
  3. cut时也要相应的减去贡献。
#include 
#include 
#include 
#include 
using namespace std;

int QQ,mask,le;
const int N = 6e5 + 10, E = 4e6 + 10;
char s[E],type[10];

void decode(int mask) {
//	cout<<"SDFSADF"<
	for (int i = 0; i < le; i++) {
		mask = (mask * 131 + i) % le;
		swap(s[i],s[mask]);
	}
}

int fail[N * 2],c[N * 2][26],tot,last,v[N * 2],len[N * 2];
int fa[N * 2], fsum[N * 2], rev[N * 2], ch[N * 2][2], tsum[N * 2];
//fsum 自己虚子树大小,tsum splay子树虚子树大小 + v, v 自己权值 

void update(int x) {
	tsum[x] = tsum[ch[x][0]] + tsum[ch[x][1]] + fsum[x] + v[x];
}

void reverse(int x) {
	if (!x) return;
	rev[x] ^= 1;
	swap(ch[x][0],ch[x][1]);
}

void down(int x) {
	if (rev[x]) {
		reverse(ch[x][0]);
		reverse(ch[x][1]);
		rev[x] = 0;
	}
}

#define gc(x) ((ch[fa[x]][1] == (x)))
#define isroot(x) ((ch[fa[x]][0] != (x)) && (ch[fa[x]][1] != (x)))

void rotate(int x) {
	int y = fa[x], z = gc(x);
	ch[y][z] = ch[x][1-z];
	if (ch[x][1-z]) fa[ch[x][1-z]] = y;

	fa[x] = fa[y];
	if (!isroot(y)) ch[fa[y]][gc(y)] = x;
	
	fa[y] = x;
	ch[x][1-z] = y;
	
	update(y);
}

int Q[N * 2];
void splay(int x) {
	int t = x;
	while (!isroot(t)) Q[++Q[0]] = t, t = fa[t];
	Q[++Q[0]] = t;
	while (Q[0]) down(Q[Q[0]--]);
	
	while (!isroot(x)) {
		int y = fa[x];
		if (!isroot(y))
			if (gc(x) == gc(y)) 
				rotate(y);
			else
				rotate(x);
		rotate(x);
	}
	
	update(x);
}

void access(int x) {
	for (int i = 0; x; i = x, x = fa[x]) {
		splay(x);
		fsum[x] -= tsum[i];
		fsum[x] += tsum[ch[x][1]];
		ch[x][1] = i;
		update(x);
	}
}

void makeroot(int x) {
	access(x);
	splay(x);
	reverse(x);
}

void link(int a, int b) {
//	printf("link %d %d\n",a,b);
	makeroot(a);
	access(a);
	splay(a);
	fa[a] = b;
	
	access(b);
	splay(b);
	
	fsum[b] += tsum[a];
	update(b);
}

void cut(int a, int b) {
//	printf("cut %d %d\n",a,b);
	makeroot(a);
	access(b);
	splay(a);
	ch[a][1] = 0, fa[b] = 0;
	
	tsum[a] -= tsum[b];
}

void insert(char r) {
	r-='A';
	int cur = ++tot; tsum[cur] = v[cur] = 1;
	len[cur] = len[last] + 1;
	
	for (; last && c[last][r] == 0; last = fail[last]) 
		c[last][r] = cur;
	if (last) {
		int p = c[last][r];
		if (len[p] == len[last] + 1) 
			link(cur,p), fail[cur] = p;
		else {
			int nep = ++tot;
			memcpy(c[nep],c[p],sizeof c[p]);
			fail[nep] = fail[p];
			cut(p, fail[p]);
			
			link(nep,fail[p]);
			link(nep, p);
			link(nep, cur);
			
			fail[p] = fail[cur] = nep;
			len[nep] = len[last] + 1;
			for (; c[last][r] == p; last = fail[last])
				c[last][r] = nep;
		}
	} else {
		link(cur,1), fail[cur] = 1;
	}
	last = cur;
}

int main() {
	//freopen("a.in","r",stdin);
	cin>>QQ;
	scanf("%s",s);
	le = strlen(s);
	tot = last = 1;
	for (int i = 0; i < le; i++) 
		insert(s[i]);
	for (int ww = 1; ww <= QQ; ww++) {
		scanf("%s %s\n",type,s);
		le = strlen(s);
		decode(mask);
		if (type[0] == 'Q') {
			int now = 1, got = 0;
			for (int i =0; i < le; i++) {
				now = c[now][s[i]-'A'];
				if (now == 0) {
					got = 1;
					printf("0\n"); break;
				}
			}
			if (got) continue;
			makeroot(1);
			access(now);
			mask ^= fsum[now] + v[now];
			printf("%d\n",fsum[now] + v[now]);
		} else {
			for (int i = 0; i < le; i++)
				insert(s[i]);
		}
	}
}

你可能感兴趣的:(新内容,题解,数据结构,树上问题)