P4075 [SDOI2016] 模式字符串 题解

考虑点分治。

路径的合并,只需要求出每个点到当前重心的字符串是否可以成为模式串依次相连的前缀或后缀即可。

这显然可以哈希维护,手推一下就知道咋做了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
using namespace std;

using ull = unsigned long long;
const int N = 1e6 + 5;
const ull BASE = 27, MOD = 1610612741;

int t, n, m;
ull hashing[N];
ull repeat_hash[N];
ull powe[N];
long long ans = 0LL;
string s;
bool del[N];
vector<int> G[N];

int sz[N], tot, wc;

void get_sz(int u, int f)
{
	sz[u] = 0;
	if (del[u]) return;
	sz[u] = 1;
	for (auto& j : G[u])
	{
		if (j ^ f)
		{
			get_sz(j, u);
			sz[u] += sz[j];
		}
	}
}

void get_wc(int u, int f)
{
	if (del[u]) return;
	int maxn = tot - sz[u];
	for (auto& j : G[u])
	{
		if (j ^ f)
		{
			get_wc(j, u);
			maxn = max(maxn, sz[j]);
		}
	}
	if (maxn <= (tot >> 1)) wc = u;
}

long long sufcnt[N], precnt[N];
ull nhash[N], revhash[N], rev_repeat_hash[N];
int cur;
char cc[N];
bool canbesuf[N], canbepre[N];
int dist[N];

vector<int> total;

ull subhash(ull* h, int l, int r)
{
	return h[r] - h[l - 1] * powe[r - l + 1];
}

void dfs_suf(int u, int f, int w)
{
	dist[u] = 0;
	cur++;
	canbesuf[u] = 0;
	if (del[u]) return;
	dist[u] = w;
	total.emplace_back(u);
	nhash[cur] = nhash[cur - 1] * BASE + (ull)(cc[u] - 'A' + 1);
	// 判断能否成为后缀
	int len = w;
	int ls = len / m;
	if (ls == 0 || subhash(nhash, len % m + 1, cur) == repeat_hash[ls])
	{
		if (len % m == 0 || subhash(nhash, 1, len % m) == subhash(hashing, m - len % m + 1, m))
		{
			canbesuf[u] = 1;
			//sufcnt[w]++;
		}
	}
	for (auto& j : G[u])
	{
		if (j ^ f)
		{
			dfs_suf(j, u, w + 1);
			cur--;
		}
	}
}

void dfs_pre(int u, int f)
{
	cur++;
	if (del[u]) return;
	nhash[cur] = nhash[cur - 1] * BASE + (ull)(cc[u] - 'A' + 1);
	canbepre[u] = 0;
	// 判断能否成为后缀
	int len = cur;
	int ls = len / m;
	if (ls == 0 || subhash(nhash, len % m + 1, cur) == rev_repeat_hash[ls])
	{
		if (len % m == 0 || subhash(nhash, 1, len % m) == subhash(revhash, m - len % m + 1, m))
		{
			canbepre[u] = 1;
			//precnt[cur]++;
		}
	}
	for (auto& j : G[u])
	{
		if (j ^ f)
		{
			dfs_pre(j, u);
			cur--;
		}
	}
}

void solve(int u)
{
	if (del[u]) return;
	cur = 0;
	get_sz(u, 0);
	tot = sz[u];
	wc = u;
	get_wc(u, 0);
	u = wc;
	del[u] = 1;
	vector<vector<int>> ntot;
	for (auto& j : G[u])
	{
		total.clear();
		total.shrink_to_fit();
		cur = 0;
		dfs_pre(j, u);
		cur = 1;
		nhash[cur] = (ull)(cc[u] - 'A' + 1);
		dfs_suf(j, u, 2);
		for (auto& k : total)
		{
			if (canbesuf[k])
			{
				if (dist[k] % m == 0)
				{
					ans++;
				}
				ans += precnt[(m - dist[k] % m) % m];
			}
			if (canbepre[k])
			{
				if (dist[k] % m == 0 && cc[u] == s[1])
				{
					ans++;
				}
				ans += sufcnt[(m - (dist[k] - 1) % m) % m];
			}
		}
		for (auto& k : total)
		{
			if (canbesuf[k]) sufcnt[dist[k] % m]++;
			if (canbepre[k]) precnt[(dist[k] - 1) % m]++;
		}
		ntot.emplace_back(total);
	}
	for (auto& j : ntot)
	{
		for (auto& k : j)
		{
			canbepre[k] = canbesuf[k] = 0;
			sufcnt[dist[k] % m] = precnt[(dist[k] - 1) % m] = 0;
			dist[k] = 0;
		}
	}
	for (auto& j : G[u]) solve(j);
}

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	powe[0] = 1;
	for (int i = 1; i < N; i++) powe[i] = powe[i - 1] * BASE;
	cin >> t;
	while (t--)
	{
		cin >> n >> m;
		for (int i = 1; i <= n; i++)
		{
			cin >> cc[i];
			G[i].clear(), G[i].shrink_to_fit();
			del[i] = 0;
		}
		for (int i = 1; i < n; i++)
		{
			int u, v;
			cin >> u >> v;
			G[u].emplace_back(v);
			G[v].emplace_back(u);
		}
		cin >> s;
		s = " " + s;
		for (int i = 1; i <= m; i++)
		{
			hashing[i] = hashing[i - 1] * BASE + (ull)(s[i] - 'A' + 1);
		}
		repeat_hash[1] = hashing[m];
		for (int i = 2; 1LL * i * m <= n; i++)
		{
			repeat_hash[i] = repeat_hash[i - 1] * powe[m] + hashing[m];
		}
		s.erase(s.begin());
		reverse(s.begin(), s.end());
		s = " " + s;
		for (int i = 1; i <= m; i++) revhash[i] = revhash[i - 1] * BASE + (ull)(s[i] - 'A' + 1);
		rev_repeat_hash[1] = revhash[m];
		for (int i = 2; 1LL * i * m <= n; i++)
		{
			rev_repeat_hash[i] = rev_repeat_hash[i - 1] * powe[m] + revhash[m];
		}
		ans = 0LL;
		solve(1);
		cout << ans << "\n";
	}
	return 0;
}

/*
1
9 2
ABAABCBCD
1 2
2 3
3 4
4 5
3 6
6 7
3 8
8 9
AB
*/
posted @   HappyBobb  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示