CF1045J Moonwalk challenge 题解

不会一点字符串算法,考虑哈希。

注意到一个特殊限制是 s100\left| s\right| \leq 100,于是我们可以简单处理出每个点向上不超过 100100 步的哈希值。

考虑询问答案时怎么处理。我们考虑 ss 在这个串,只有三种。令 L=LCA(u,v)L=\operatorname{LCA}(u,v),则这个串要么在 uLu \sim L 中,要么在 LvL \sim v 中,要么跨过 LL。前两种可以转化成树上一条链的某个数出现次数,考虑离线或者在线用树剖和 vector 上二分维护。最后一种,可以注意到 LL 到两侧的那段多出来的长度不超过 s100\left| s\right| \leq 100,暴力维护即可。

记得用双哈希并且卡常。

#pragma comment(linker, "/stack:200000000")
#pragma GCC optimize("-Ofast,fast-math,-inline")
#pragma GCC target("sse,sse2,sse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include <iostream>
#include <cstdio>
#include <iomanip>
#include <map>
#include <unordered_map>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
using namespace std;

const int N = 1e5 + 5;
const long long MOD = 998244853ll, MOD2 = 998244353ll;

int n, q;
vector<pair<int, char>> G[N];
struct Two_Hash
{
	int hs1, hs2;
	Two_Hash() = default;
	Two_Hash(int hs1, int hs2) : hs1(hs1), hs2(hs2) {}
	Two_Hash operator+(const int& x) const
	{
		return Two_Hash((hs1 + x) % MOD, (hs2 + x) % MOD2);
	}
	Two_Hash operator*(const int& x) const
	{
		return Two_Hash((hs1 * 1ll * x) % MOD, (hs2 * 1ll * x) % MOD2);
	}
	Two_Hash operator*(const Two_Hash& x) const
	{
		return Two_Hash((1ll * hs1 * x.hs1) % MOD, (1ll * hs2 * x.hs2) % MOD2);
	}
	Two_Hash operator+(const Two_Hash& x) const
	{
		return Two_Hash((hs1 + x.hs1) % MOD, (hs2 + x.hs2) % MOD2);
	}
	bool operator==(const Two_Hash& x) const
	{
		return hs1 == x.hs1 && hs2 == x.hs2;
	}
	void operator=(const int x)
	{
		hs1 = x;
		hs2 = x;
	}
};

struct Myhash
{
	size_t operator()(const Two_Hash& x) const
	{
		return (1ll * x.hs1 * MOD + x.hs2) % MOD2;
	}
};

Two_Hash f[N][101], g[N][101];

int fa[N], nfa[N][21], dis[N];
Two_Hash powe[N];
int dep[N];

static void dfs(int u, int f)
{
	nfa[u][0] = f;
	fa[u] = f;
	dep[u] = dep[f] + 1;
	for (auto& [j, c] : G[u])
	{
		if (j == f) continue;
		::f[j][1] = c - 'a' + 1;
		::g[j][1] = c - 'a' + 1;
		dis[j] = dis[u] + 1;
		dfs(j, u);
	}
}

inline int LCA(int u, int v)
{
	if (u == v) return u;
	if (dep[u] < dep[v]) swap(u, v);
	int k(dep[u] - dep[v]), c = 0;
	while (k)
	{
		if (k & 1) u = nfa[u][c];
		k >>= 1, c++;
	}
	if (u == v) return u;
	for (int i = 16; i >= 0; i--)
	{
		if (nfa[u][i] ^ nfa[v][i]) u = nfa[u][i], v = nfa[v][i];
	}
	return nfa[u][0];
}

int id[N], top[N], sz[N], son[N], idx;

class TreeCut
{
public:
	int pp;
	vector<int> dfn;
	unordered_map<Two_Hash, vector<int>, Myhash> pos;
	inline void dfs1(int u, int f)
	{
		fa[u] = f;
		sz[u] = 1;
		dep[u] = dep[f] + 1;
		for (auto& [j, c] : G[u])
		{
			if (j == f) continue;
			dfs1(j, u);
			sz[u] += sz[j];
			if (sz[son[u]] < sz[j]) son[u] = j;
		}
	}
	inline void dfs2(int u, int f)
	{
		top[u] = f;
		id[u] = ++idx;
		dfn.emplace_back(u);
		pos[::f[u][pp]].emplace_back(idx);
		if (!son[u]) return;
		dfs2(son[u], f);
		for (auto& [j, c] : G[u])
		{
			if ((j ^ fa[u]) and (j ^ son[u])) dfs2(j, j);
		}
	}
	inline int qry(int l, int r, Two_Hash v)
	{
		if (!pos.count(v)) return 0;
		return upper_bound(pos[v].begin(), pos[v].end(), r) - lower_bound(pos[v].begin(), pos[v].end(), l);
	}
	inline int query(int u, int v, Two_Hash c)
	{
		int res = 0;
		while (top[u] ^ top[v])
		{
			if (dep[top[u]] < dep[top[v]]) swap(u, v);
			res += qry(id[top[u]], id[u], c);
			u = fa[top[u]];
		}
		if (dep[u] > dep[v]) swap(u, v);
		res += qry(id[u], id[v], c);
		return res;
	}
	inline void build(int g)
	{
		pos.clear();
		pp = g;
		if (g == 1) dfs1(1, 1), dfs2(1, 1);
		else
		{
			for (auto& i : dfn) pos[::f[i][pp]].emplace_back(id[i]);
		}
	}
}tc;

inline Two_Hash gethash(string s)
{
	Two_Hash tw;
	tw.hs1 = tw.hs2 = 0;
	for (auto& i : s)
	{
		tw = (tw * 26ll + (i - 'a' + 1));
	}
	return tw;
}

struct Query
{
	int u, v;
	Two_Hash c;
	int id;
	Query() = default;
	Query(int u, int v, Two_Hash c, int id) :u(u), v(v), c(c), id(id) {}
};

vector<Query> vq[105];
int res[N];

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n;
	powe[0] = 1;
	for (int i = 1; i < N; i++) powe[i] = powe[i - 1] * 26ll;
	for (int i = 1; i < n; i++)
	{
		int u, v;
		char c;
		cin >> u >> v >> c;
		G[u].emplace_back(make_pair(v, c));
		G[v].emplace_back(make_pair(u, c));
	}
	dfs(1, 1);
	for (int j = 1; j <= 16; j++)
	{
		for (int i = 1; i <= n; i++) nfa[i][j] = nfa[nfa[i][j - 1]][j - 1];
	}
	auto get_k_fa = [&](int u, int k) -> int
		{
			int c = 0;
			while (k)
			{
				if (k & 1) u = nfa[u][c];
				c++;
				k >>= 1;
			}
			return u;
		};
	auto get_dis = [&](int u, int v) -> int
		{
			return dis[u] + dis[v] - 2 * dis[LCA(u, v)];
		};
	for (int j = 1; j <= 100; j++)
	{
		for (int i = 1; i <= n; i++)
		{
			int ff = get_k_fa(i, j - 1);
			f[i][j] = (f[ff][1] * powe[j - 1] + f[i][j - 1]);
			g[i][j] = (g[i][j - 1] * 26ll + g[ff][1]);
		}
	}
	cin >> q;
	for (int i = 1; i <= q; i++)
	{
		int u, v;
		string s;
		cin >> u >> v >> s;
		int L(LCA(u, v));
		Two_Hash rr(gethash(s));
		// L->v
		int rv = v;
		int ans = 0;
		int dul = get_dis(u, L), dul2 = get_dis(v, L), len = s.size();
		if (dul2 <= len)
		{
			int rd(dul2);
			int dd = dul + dul2;
			vector<int> tot;
			for (; v != L; v = fa[v])
			{
				if (dd < len) break;
				dd--;
				tot.emplace_back(v);
				rd--;
			}
			v = L;
			dd = get_dis(v, u);
			int p, npos;
			if (dd >= len)
			{
				tot.emplace_back(L);
				rd--;
			}
			reverse(tot.begin(), tot.end());
			if (tot.empty()) continue;
			p = len - rd - 1;
			npos = get_k_fa(u, dul - p);
			for (auto& v : tot)
			{
				rd++;
				ans += ((g[npos][len - rd] * powe[rd] + f[v][rd]) == rr);
				npos = fa[npos];
			}
		}
		else
		{
			int lens = get_dis(v, L);
			int pos(get_k_fa(v, lens - len - 1));
			vq[len].emplace_back(Query(pos, v, rr, i));
			v = nfa[pos][0];
			int rd = get_dis(v, L);
			int dd = get_dis(v, u);
			vector<int> tot;
			for (; v != L; v = fa[v])
			{
				if (dd < len) break;
				dd--;
				tot.emplace_back(v);
				rd--;
			}
			v = L;
			dd = get_dis(v, u);
			int p, npos;
			if (dd >= len)
			{
				tot.emplace_back(L);
				rd--;
			}
			reverse(tot.begin(), tot.end());
			if (tot.empty()) continue;
			p = len - rd - 1;
			npos = get_k_fa(u, dul - p);
			for (auto& v : tot)
			{
				rd++;
				ans += ((g[npos][len - rd] * powe[rd] + f[v][rd]) == rr);
				npos = fa[npos];
			}
		}
		// L->u
		reverse(s.begin(), s.end());
		v = rv;
		if (dul > len)
		{
			vq[len].emplace_back(Query(u, get_k_fa(u, dul - len - 1), gethash(s), i));
		}
		res[i] = ans;
	}
	for (int i = 1; i <= 100; i++)
	{
		if (!vq[i].size()) continue;
		tc.build(i);
		for (auto& [u, v, c, id] : vq[i])
		{
			res[id] += tc.query(u, v, c);
		}
	} // 1
	for (int i = 1; i <= q; i++) cout << res[i] << "\n";
	return 0;
}
posted @   HappyBobb  阅读(6)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示