ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CodeForces 587F Duff is Mad

2022-07-01 23:02:35  阅读:159  来源: 互联网

标签:qq 结点 ch 587F int CodeForces maxn Mad fail


洛谷传送门

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有