其他分享
首页 > 其他分享> > CodeForces 587F Duff is Mad

CodeForces 587F Duff is Mad

作者:互联网

洛谷传送门

CF 传送门

CF547E 略难的字符串好题。

思路

首先令 \(m = \sum\limits_{i=1}^n |s_i|\)。

设 \(a_i\) 为第 \(i\) 个字符串在 AC 自动机上的终止结点。考虑在 AC 自动机上匹配的过程,\(x\) 在 \(y\) 中出现的次数就相当于在 Trie 树上 \(a_y\) 到根结点的链上,每个结点都不断跳 \(\mathrm{fail}\),有多少个结点是 \(a_x\),也就是在 \(\mathrm{fail}\) 树上,有多少个结点在 \(a_x\) 的子树内。

如果你在做 CF547E,想到这一步就结束了。但这题求的是 \(s_{l...r}\) 在 \(s_k\) 中的出现次数,即 Trie 树上 \(a_k\) 到根结点链上的每个结点,在 \(a_{l...r}\) 的子树内的出现次数,显然不能直接暴力处理。考虑根号分治,设一个阈值 \(B\)。

当 \(|s_k| > B\) 时,不难发现满足此要求的 \(k\) 是 \(O(\frac{m}{B})\) 级别的。因此每个 \(k\) 可以 \(O(m)\) 处理。具体地,处理每个 \(k\) 都将 \(a_k\) 到根结点的链上的点的 \(\mathrm{size}\) 设为 \(1\),再一遍 dfs 求出子树和,那么询问 \((l,r,k)\) 的答案即为 \(\sum\limits_{i=l}^r \mathrm{size}_{a_i}\),前缀和预处理后 \(O(1)\) 回答每个询问。这部分的时间复杂度为 \(O(\frac{m^2}{B})\)。

当 \(|s_k| \le B\) 时,这意味着每个询问可以 \(O(|s_k|)\) 处理。设一个 \(val\) 值,将每个询问 \((l,r,k)\) 拆成 \((1,r,k) - (1,l-1,k)\),然后遍历每个字符串,设当前遍历到 \(i\),就将 \(a_i\) 在 \(\mathrm{fail}\) 树上的子树的 \(val\) 加一,处理右端点为 \(i\) 的询问时,就直接暴力遍历 Trie 树上 \(a_k\) 到根结点的链,累加所有结点的 \(val\)。我们需要一个支持区间加,单点查的数据结构。因为单点查的次数是 \(O(qB)\) 级别的,所以使用区间加 \(O(\sqrt{m})\)、单点查 \(O(1)\) 的分块则这部分复杂度最优,为 \(O(n \sqrt{m} + qB)\)。

总时间复杂度为 \(O(n \sqrt{m} + \frac{m^2}{B} + qB)\)。要使 \(\max(\frac{m^2}{B},qB)\) 最小化,显然在函数图像上取交点最优,所以 \(B = \frac{m}{\sqrt{q}}\),此时总时间复杂度为 \(O(n \sqrt{m} + m \sqrt{q})\)。

代码

code
/*

p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy

*/

#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second

using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;

const int maxn = 100100;

int n, m, q, len[maxn], idx[maxn], sz[maxn];
int bel[maxn], block, lb[maxn], rb[maxn];
ll val[maxn], tag[maxn], ans[maxn], sum[maxn];
int B;
int head[maxn], elen;
int st[maxn], times, ed[maxn];
char s[maxn];

struct query {
	int l, r, k;
} qq[maxn];

struct edge {
	int to, next;
} edges[maxn << 1];

void add_edge(int u, int v) {
	edges[++elen].to = v;
	edges[elen].next = head[u];
	head[u] = elen;
}

struct node {
	int op, x, id;
	node() {}
	node(int a, int b, int c) : op(a), x(b), id(c) {}
};
vector<node> qa[maxn];

bool cmp(node a, node b) {
	return a.x < b.x;
}

void update(int l, int r, ll x) {
	if (bel[l] == bel[r]) {
		for (int i = l; i <= r; ++i) {
			val[i] += x;
		}
		return;
	}
	for (int i = l; i <= rb[bel[l]]; ++i) {
		val[i] += x;
	}
	for (int i = bel[l] + 1; i < bel[r]; ++i) {
		tag[i] += x;
	}
	for (int i = lb[bel[r]]; i <= r; ++i) {
		val[i] += x;
	}
}

ll query(int x) {
	return val[x] + tag[bel[x]];
}

struct AC {
	int fail[maxn], fa[maxn], tot, ch[maxn][26];
	
	void init() {
		tot = 0;
		memset(fail, 0, sizeof(fail));
		memset(fa, 0, sizeof(fa));
		memset(ch, 0, sizeof(ch));
	}
	
	void insert(char *s, int id) {
		int p = 0;
		for (int i = 0; s[i]; ++i) {
			if (!ch[p][s[i] - 'a']) {
				ch[p][s[i] - 'a'] = ++tot;
				fa[tot] = p;
			}
			p = ch[p][s[i] - 'a'];
		}
		idx[id] = p;
	}
	
	void build() {
		queue<int> q;
		for (int i = 0; i < 26; ++i) {
			if (ch[0][i]) {
				q.push(ch[0][i]);
			}
		}
		while (q.size()) {
			int u = q.front();
			q.pop();
			for (int i = 0; i < 26; ++i) {
				if (ch[u][i]) {
					fail[ch[u][i]] = ch[fail[u]][i];
					q.push(ch[u][i]);
				} else {
					ch[u][i] = ch[fail[u]][i];
				}
			}
		}
		for (int i = 1; i <= tot; ++i) {
			add_edge(fail[i], i);
		}
	}
	
	void dfs(int u) {
		for (int i = head[u]; i; i = edges[i].next) {
			int v = edges[i].to;
			dfs(v);
			sz[u] += sz[v];
		}
	}
	
	void dfs2(int u) {
		st[u] = ++times;
		for (int i = head[u]; i; i = edges[i].next) {
			int v = edges[i].to;
			dfs2(v);
		}
		ed[u] = times;
	}
} ac;

void solve() {
	ac.init();
	scanf("%d%d", &n, &q);
	for (int i = 1; i <= n; ++i) {
		scanf("%s", s);
		len[i] = strlen(s);
		m += len[i];
		ac.insert(s, i);
	}
	ac.build();
	B = sqrt(1LL * m * m / q);
	for (int i = 1; i <= q; ++i) {
		scanf("%d%d%d", &qq[i].l, &qq[i].r, &qq[i].k);
	}
	for (int i = 1; i <= q; ++i) {
		if (len[qq[i].k] > B) {
			if (qq[i].l > 1) {
				qa[qq[i].k].pb(node(-1, qq[i].l - 1, i));
			}
			qa[qq[i].k].pb(node(1, qq[i].r, i));
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (qa[i].empty()) {
			continue;
		}
		for (int u = idx[i]; u; u = ac.fa[u]) {
			sz[u] = 1;
		}
		ac.dfs(0);
		for (int j = 1; j <= n; ++j) {
			sum[j] = sum[j - 1] + sz[idx[j]];
		}
		for (node p : qa[i]) {
			ans[p.id] += 1LL * p.op * sum[p.x];
		}
		qa[i].clear();
		for (int u = 0; u <= ac.tot; ++u) {
			sz[u] = 0;
		}
	}
	ac.dfs2(0);
	block = sqrt(times);
	for (int i = 1; i <= times; ++i) {
		bel[i] = (i - 1) / block + 1;
	}
	for (int i = 1; i <= times; ++i) {
		if (!lb[bel[i]]) {
			lb[bel[i]] = i;
		}
		rb[bel[i]] = i;
	}
	for (int i = 1; i <= q; ++i) {
		if (len[qq[i].k] <= B) {
			if (qq[i].l > 1) {
				qa[qq[i].l - 1].pb(node(-1, qq[i].k, i));
			}
			qa[qq[i].r].pb(node(1, qq[i].k, i));
		}
	}
	for (int i = 1; i <= n; ++i) {
		update(st[idx[i]], ed[idx[i]], 1);
		for (node p : qa[i]) {
			for (int u = idx[p.x]; u; u = ac.fa[u]) {
				ans[p.id] += 1LL * p.op * query(st[u]);
			}
		}
	}
	for (int i = 1; i <= q; ++i) {
		printf("%lld\n", ans[i]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:qq,结点,ch,587F,int,CodeForces,maxn,Mad,fail
来源: https://www.cnblogs.com/zltzlt-blog/p/16436194.html