问题
给一棵树,每个节点有一个小写字母。多次询问,每次给出两个节点 ,问将
到
路径上的所有字母重新排列,能组成的最长回文串的长度是多少。
思路
一个字符串能重排成回文串当且仅当至多一个字母出现奇数次。
最长回文串的长度 = 所有字母出现次数的偶数部分之和(即每对相同字母贡献2),如果存在奇数个的字母,可额外加1。
因此只需统计路径上每个字母的出现次数。
实现
用 DFS 预处理每个节点到根路径上各个字母的前缀和 cnt[u][c]。
用倍增法预处理 LCA,快速求祖先。
对于询问 ,设
,
(
的父亲),则路径上的字母出现次数为:
num=cnt[x][c]+cnt[y][c]−cnt[l][c]−cnt[p][c]
累加每对贡献,判断是否有奇数。
复杂度
预处理 ,单次询问
,
总时间 ,空间
。
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
using namespace std;
const int MAXN = 2e5 + 5;
const int LOG = 20; // 2^20 > 2e5
// ---------- LCA 倍增预处理 ----------
vector<int> G[MAXN];
int depth[MAXN];
int up[MAXN][LOG];
void dfs_lca(int u, int p) {
depth[u] = depth[p] + 1;
up[u][0] = p;
for (int i = 1; i < LOG; i++)
up[u][i] = up[ up[u][i-1] ][i-1];
for (int v : G[u]) if (v != p)
dfs_lca(v, u);
}
int lca(int u, int v) {
if (depth[u] < depth[v]) swap(u, v);
int diff = depth[u] - depth[v];
for (int i = 0; i < LOG; i++)
if (diff >> i & 1) u = up[u][i];
if (u == v) return u;
for (int i = LOG-1; i >= 0; i--)
if (up[u][i] != up[v][i])
u = up[u][i], v = up[v][i];
return up[u][0];
}
// ---------- 树上字符前缀和 ----------
int cnt[MAXN][26]; // cnt[u][c] : 从根到 u 的路径上字母 c 的出现次数
int fa[MAXN]; // 父节点
void dfs_cnt(int u, int p, const string& s) {
fa[u] = p;
for (int c = 0; c < 26; c++)
cnt[u][c] = cnt[p][c];
cnt[u][ s[u] - 'a' ]++;
for (int v : G[u]) if (v != p)
dfs_cnt(v, u, s);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n; cin >> n;
for (int i = 1; i < n; i++) {
int u, v; cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
string s; cin >> s;
s = " " + s; // 1‑based
// 预处理 LCA
depth[0] = -1;
dfs_lca(1, 0);
// 预处理前缀和
dfs_cnt(1, 0, s);
int q; cin >> q;
while (q--) {
int x, y; cin >> x >> y;
int anc = lca(x, y);
int par = fa[anc]; // anc 的父亲
int pair_cnt = 0; // 能配成多少对相同字母
int odd_flag = 0; // 是否有奇数个的字母
for (int c = 0; c < 26; c++) {
int num = cnt[x][c] + cnt[y][c] - cnt[anc][c] - cnt[par][c];
pair_cnt += num / 2;
if (num % 2) odd_flag = 1;
}
// 最长回文串长度 = 2 * 对数 + (有奇数个时加 1)
cout << pair_cnt * 2 + odd_flag << '\n';
}
return 0;
}

京公网安备 11010502036488号