Luogu P5212 【SubString】

Description

传送门


Solution

动态加入字符就用\(SAM\),发现答案就是一个点的子树的\(siz\)之和,所以需要动态维护子树和,上\(LCT\)

\(lCT\)上每个节点,\(siz\)表示\(Splay\)上大小,\(lsiz\)表示虚子树大小,修改\(Update\)\(Access\)\(Link\)函数进行修改即可。

最后答案就是\(Splay\)上深度比它大的节点的\(siz\)之和加上它的虚子树大小,如果它是字符串的前缀,再加一。


Code

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 1200050;
const int INF = 999999999;

int fa[N], son[N][26], last = 1, cnt = 1, len[N], rev[N], n, mask, ans;

char s[3000050];

struct LCT
{
	int son[2], siz, lsiz, fa, v;
} tr[N + 50];

bool Is(int x)
{
	return tr[tr[x].fa].son[1] != x && tr[tr[x].fa].son[0] != x;
}

int Get(int x)
{
	return tr[tr[x].fa].son[1] == x;
}

void Rv(int x)
{
	rev[x] ^= 1;
	swap(tr[x].son[0], tr[x].son[1]);
	return;
}

void Pushdown(int x)
{
	if (rev[x])
	{
		if (tr[x].son[0]) Rv(tr[x].son[0]);
		if (tr[x].son[1]) Rv(tr[x].son[1]);
		rev[x] = 0;
		return;
	}
}
void Update(int x)
{
	tr[x].siz = tr[tr[x].son[0]].siz + tr[tr[x].son[1]].siz + tr[x].lsiz + tr[x].v;
	return;
}

void Rotate(int x)
{
	int f = tr[x].fa, p = tr[f].fa, d = Get(x);
	if (!Is(f)) tr[p].son[Get(f)] = x;
	tr[x].fa = p;
	tr[f].son[d] = tr[x].son[d ^ 1];
	if (tr[f].son[d]) tr[tr[f].son[d]].fa = f;
	tr[x].son[d ^ 1] = f;
	tr[f].fa = x;
	Update(f); 
	Update(x);
	return;
}//旋转,记得判断父亲是不是splay的根 

void Calc(int x)
{
	if (!Is(x)) Calc(tr[x].fa);
	Pushdown(x);
	return;
}//递归处理标记 

void Splay(int x)
{
	Calc(x);
	while (!Is(x))
	{
		int f = tr[x].fa;
		if (!Is(f))
			Get(f) ^ Get(x) ? Rotate(x) : Rotate(f);
		Rotate(x);
	}
	return;
}

void Access(int x)
{
	int p = 0;
	while (x)
	{
		Splay(x);
		tr[x].lsiz += tr[tr[x].son[1]].siz - tr[p].siz;
		tr[x].son[1] = p;
		Update(x);
		p = x;
		x = tr[x].fa;
	}
	return;
}

void Makeroot(int x)
{
	Access(x);
	Splay(x);
	Rv(x); 
	return;
}

int Findroot(int x)
{
	Access(x);
	Splay(x);
	while (tr[x].son[0]) Pushdown(x), x = tr[x].son[0];
	Splay(x);
	return x;
}

void Link(int u, int v)
{
	Makeroot(u);
	if (Findroot(v) == u) return;
	tr[u].fa = v; tr[v].lsiz += tr[u].siz;
	Update(v);
	return;
}

void Cut(int u, int v)
{
	Makeroot(u);
	if (Findroot(v) != u || tr[v].fa != u || tr[v].son[0]) return;
	tr[u].son[1] = tr[v].fa = 0;
	Update(u);
	return;
}

int Askk(int x)
{
	Makeroot(1); Access(x); Splay(x);
	return tr[x].lsiz + tr[x].v + tr[tr[x].son[1]].siz;
}

void Init(int x)
{
	tr[x].siz = tr[x].v = 1;
	return;
}

void Change(int x, int y)
{
	if (fa[x]) Cut(x, fa[x]);
	Link(x, fa[x] = y);
	return;
}

void Insert(int c)
{
	int p = last, ne = last = ++cnt;
	len[ne] = len[p] + 1; Init(ne);
	while (p && !son[p][c]) son[p][c] = ne, p = fa[p];
	if (!p) { Change(ne, 1); return; }
	int q = son[p][c];
	if (len[q] == len[p] + 1) { Change(ne, q); return; }
	int sp = ++cnt;
	for (int i = 0; i < 26; i++) son[sp][i] = son[q][i];
	len[sp] = len[p] + 1;
	Change(sp, fa[q]);
	Change(q, sp); Change(ne, sp);
	while (p && son[p][c] == q) son[p][c] = sp, p = fa[p];
	return;
}

void Work()
{
	scanf("%s", s); n = strlen(s);
//	return;
	int rec = mask;
	for (int i = 0; i < n; i++)
	{
		rec = (rec * 131 + i) % n;
		swap(s[rec], s[i]);
	}
	return;
}

void Ask()
{
	int now = 1, ans = 0;
	Work();
	for (int i = 0; i < n && now; i++) now = son[now][s[i] - 'A'];
	if (!now) puts("0");
	else printf("%d\n", ans = Askk(now));
	mask ^= ans;
	return;
}

void Add()
{
	Work();
	for (int i = 0; i < n; i++) Insert(s[i] - 'A');
	return;
}

int main()
{
	int t;
	char st[10];
	scanf("%d", &t);
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for (int i = 1; i <= n; i++) Insert(s[i] - 'A');
	while (t--)
	{
		scanf("%s", st + 1);
		if (st[1] == 'Q') Ask();
		else Add();
	}
	return 0;
}
posted @ 2020-06-16 07:25  Tian-Xing  阅读(128)  评论(0编辑  收藏  举报