CF914E Palindromes in a Tree 题解
一个经典套路是,一个字符串如果重排后可以变成回文,那么其充要条件是出现次数为奇数的字符不超过 个。证明显然。
注意到题目的字符集是 a 到 t,这之间总共 个字符。我们只关心一个字符的出现次数的奇偶性,而非具体值。也就是说, 和 本质等价。不妨考虑状态压缩,每一位储存这个字符出现次数取模 的结果。
然后就可以愉快点分治了。设 表示 到分治中学的路径的上述状态压缩的结果。容易发现,两条路径拼接相当于两个值异或,对于 ,那么 。对于每个点,枚举 ,容易知道 是 的所有数,可以约 次暴力枚举。然后套上两点路径上每个点点权 的做法,就可以做到 。
注意两条路径合并,分治重心是唯一一个贡献错误的点,需要重新计算。
// LUOGU_RID: 123808133
#pragma GCC optimize("-Ofast,fast-math,-inline")
#pragma GCC target("avx,sse,sse2,sse3,popcnt")
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
using namespace std;
#ifndef ONLINE_JUDGE
#define __builtin_popcount __popcnt
#endif // !ONLINE_JUDGE
const int N = 2e5 + 5, M = 2e6 + 5;
char c[N];
int n;
vector<int> G[N];
class SegmentTree
{
public:
long long cf[N], ans[N];
void update(int l, int r, long long v)
{
cf[l] += v;
cf[r + 1] -= v;
}
void getans()
{
for (int i = 1; i <= n; i++) ans[i] = ans[i - 1] + cf[i];
}
};
class TreeCut
{
public:
int dep[N], fa[N], sz[N], son[N], top[N], id[N], idx;
SegmentTree sgt;
void dfs1(int u, int f)
{
dep[u] = dep[f] + 1;
fa[u] = f;
sz[u] = 1;
for (auto& j : G[u])
{
if (j ^ f)
{
dfs1(j, u);
sz[u] += sz[j];
if (sz[son[u]] < sz[j]) son[u] = j;
}
}
}
void dfs2(int u, int f)
{
top[u] = f;
id[u] = ++idx;
if (!son[u]) return;
dfs2(son[u], f);
for (auto& j : G[u])
{
if ((j ^ son[u]) && (j ^ fa[u])) dfs2(j, j);
}
}
void build()
{
dfs1(1, 1);
dfs2(1, 1);
//sgt.build(1, 1, n);
}
void update(int u, int v, long long k)
{
//cout << "!!: " << u << " " << v << " " << k << "\n";
if (!k) return;
while (top[u] ^ top[v])
{
if (dep[top[u]] < dep[top[v]]) swap(u, v);
sgt.update(id[top[u]], id[u], k);
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
sgt.update(id[u], id[v], k);
}
}tc;
int p[N];
long long cnt[M];
int cur;
vector<int> v[N], vv[N];
int sz[N];
bool del[N];
int tot, wc;
void get_size(int u, int f)
{
sz[u] = 0;
if (del[u]) return;
sz[u] = 1;
for (auto& j : G[u])
{
if (j ^ f) get_size(j, u), sz[u] += sz[j];
}
}
void calc_wc(int u, int f)
{
if (del[u]) return;
int maxn = tot - sz[u];
for (auto& j : G[u])
{
if (j ^ f)
{
calc_wc(j, u);
maxn = max(maxn, sz[j]);
}
}
if (maxn <= (tot >> 1)) wc = u;
}
long long nc = 0;
void dfs(int u, int f)
{
if (del[u]) return;
p[u] = p[f] ^ (1 << (c[u] - 'a'));
cnt[p[u]]++;
v[cur].emplace_back(u);
if (v[cur].size() == 1) vv[cur].emplace_back(u);
for (auto& j : G[u])
{
if (j ^ f) dfs(j, u);
}
}
long long res[N];
void solve(int u)
{
if (del[u]) return;
get_size(u, 0);
nc = 0;
tot = sz[u];
cur = 0;
wc = 0;
calc_wc(u, 0);
u = wc;
del[u] = 1;
p[u] = (1 << (c[u] - 'a'));
//cnt[p[u]]++;
for (auto& j : G[u])
{
if (del[j]) continue;
cur++;
v[cur].clear();
vv[cur].clear();
dfs(j, u);
}
int ncur = 0;
for (auto& j : G[u])
{
if (del[j]) continue;
ncur++;
for (auto& j : v[ncur])
{
cnt[p[j]]--;
}
for (auto& j : v[ncur])
{
if (__builtin_popcount(p[j]) <= 1)
{
tc.update(u, j, 1LL);
}
for (int i = 0; i < 20; i++)
{
int g = (1 << i);
int k = (g ^ p[j]) ^ (1 << (c[u] - 'a'));
if (cnt[k] > 0)
{
nc += cnt[k];
tc.update(vv[ncur][0], j, cnt[k]);
}
}
int g = 0;
int k = (g ^ p[j]) ^ (1 << (c[u] - 'a'));
if (cnt[k] > 0)
{
nc += cnt[k];
tc.update(vv[ncur][0], j, cnt[k]);
}
}
for (auto& j : v[ncur])
{
cnt[p[j]]++;
}
}
for (int i = 1; i <= cur; i++)
{
for (auto& j : v[i]) cnt[p[j]] = 0;
}
tc.update(u, u, (nc >> 1LL));
for (auto& j : G[u]) solve(j);
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
G[u].emplace_back(v);
G[v].emplace_back(u);
}
for (int i = 1; i <= n; i++) cin >> c[i];
tc.build();
solve(1);
tc.sgt.getans();
for (int i = 1; i <= n; i++) cout << tc.sgt.ans[tc.id[i]] + 1LL << " ";
return 0;
}

浙公网安备 33010602011771号