【BZOJ 3881】【COCI 2015】Divljak

http://www.lydsy.com/JudgeOnline/problem.php?id=3881
好难的一道题啊qwq
一开始我想对T建AC自动机,根本不可做。
正解是对S建AC自动机。
fail树的性质:一棵子树中所有的点都有子树的根这个后缀。
对于要插入的一个串\(P_x\),我们在AC自动机上匹配它。
考虑问题要问\(S_x\)是多少\(P_x\)的子串,子串可以表示成一个前缀的后缀。
匹配过程中经过的所有点都可以当做\(P_x\)的一个前缀,暴力做法是对每个当做\(P_x\)前缀的点暴力沿着fail指针往上跳,对经过的所有点染上一种颜色。
回答询问就是回答代表\(S_x\)的结点有多少不同的颜色。
对于上面那个暴力做法,可以在每个代表\(P_x\)前缀的结点上打上颜色,回答询问直接统计子树里有多少种不同的颜色即可。
可以用bits维护dfs序,但在一棵子树里同一种颜色可能有两个,对这棵子树的dfs序贡献必须是1。
有一个非常神奇的东西,把当前所有要打上颜色的结点按dfs序排序,每个节点上+1,排序后相邻结点的lca出-1。
这样对于一棵子树里的所有颜色相同的结点,它们总的贡献是1。
好神啊,不过\(2*10^6\)能用\(O(n\log n)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 2000003;

struct node {int nxt, to;} E[N];
int id[N], qu[N], ch[N][26], cnt = 0, cnt2 = 1, fail[N], point[N];
void ins(int u, int v) {E[++cnt] = (node) {point[u], v}; point[u] = cnt;}

void insert(int num, char *s) {
	int x, tmp = 1, len = strlen(s);
	for (int i = 0; i < len; ++i) {
		x = s[i] - 'a';
		if (ch[tmp][x] != 0) tmp = ch[tmp][x];
		else tmp = ch[tmp][x] = ++cnt2;
	}
	id[num] = tmp;
}

void BFS() {
	int p = 0, q = 1, f, x, v; qu[1] = 1;
	while (p != q) {
		x = qu[++p];
		for (int i = 0; i < 26; ++i)
			if (ch[x][i]) {
				v = qu[++q] = ch[x][i];
				f = fail[x];
				while (f && ch[f][i] == 0)
					f = fail[f];
				fail[v] = f ? ch[f][i] : 1;
				ins(fail[v], v);
			}
	}
}

int tot = 0, n, deep[N], L[N], R[N], sz[N], top[N], son[N], fa[N];
char s[N];

void dfs(int x) {
	L[x] = ++tot; sz[x] = 1;
	for (int i = point[x], v = E[i].to; i; v = E[i = E[i].nxt].to) {
		fa[v] = x; deep[v] = deep[x] + 1;
		dfs(v); sz[x] += sz[v];
		if (son[x] == 0 || sz[v] > sz[son[x]])
			son[x] = v;
	}
	R[x] = tot;
}

void dfs2(int x) {
	if (son[x]) {
		top[son[x]] = top[x];
		dfs2(son[x]);
	}
	for (int i = point[x], v = E[i].to; i; v = E[i = E[i].nxt].to)
		if (v != son[x])
			top[v] = v, dfs2(v);
}

int LCA(int x, int y) {
	while (top[x] != top[y]) {
		if (deep[top[x]] < deep[top[y]])
			swap(x, y);
		x = fa[top[x]];
	}
	return deep[x] < deep[y] ? x : y;
}

bool cmp(int x, int y) {return L[x] < L[y];}
int bits[N], a[N];

void update(int x, int d) {
	for (; x <= tot; x += (x & (-x)))
		bits[x] += d;
}
int sum(int x) {
	int ret = 0;
	for (; x; x -= (x & (-x)))
		ret += bits[x];
	return ret;
}

void add(char *s) {
	int len = strlen(s), x, tmp = 1, tt = 0;
	for (int i = 0; i < len; ++i) {
		x = s[i] - 'a';
		if (ch[tmp][x]) tmp = ch[tmp][x];
		else {
			while (tmp && ch[tmp][x] == 0) tmp = fail[tmp];
			if (ch[tmp][x]) tmp = ch[tmp][x];
			else tmp = 1;
		}
		a[++tt] = tmp;
		update(L[tmp], 1);
	}
	stable_sort(a + 1, a + tt + 1, cmp);
	for (int i = 2; i <= tt; ++i)
		update(L[LCA(a[i - 1], a[i])], -1);
}

int Sum(int x) {
	return sum(R[x]) - sum(L[x] - 1);
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%s", s);
		insert(i, s);
	}
	BFS();
	dfs(1);
	top[1] = 1; dfs2(1);
	
	int q, op;
	scanf("%d", &q);
	while (q--) {
		scanf("%d", &op);
		if (op == 1) {
			scanf("%s", s);
			add(s);
		} else {
			scanf("%d", &op);
			printf("%d\n", Sum(id[op]));
		}
	}
	return 0;
}
posted @ 2017-01-17 09:51  abclzr  阅读(706)  评论(0编辑  收藏  举报