题解:P9233 [蓝桥杯 2023 省 A] 颜色平衡树
A7F3jK9pR0xf_ · · 题解
题目传送门
思路
先考虑暴力。显然对每颗子树做一遍 dfs 判定,如何判定?可以把
使用 dsu on tree 优化。我们不期望重儿子的子树遍历多次,因为它的节点数最多,损耗的时间最大。于是我们可以把每次重儿子遍历后的状态都保存不动,然后遍历非重儿子的子树,储存计算完后再删掉。因为轻边边数为
Code
以下设
#include <bits/stdc++.h>
using namespace std;
#define il inline
#define pb push_back
const int N = 2e5 + 10;
vector<int> g[N];
int a[N], b[N], c[N], typ, ans, n;
int dp[N], siz[N], son[N], dfn[N], rdfn[N], dfscnt;
il void add(int x)
{
c[b[x]]--, c[++b[x]]++;
if(b[x] == 1) typ++;
}
il void del(int x)
{
c[b[x]]--, c[--b[x]]++;
if(!b[x]) typ--;
}
il void dfs1(int u)
{
dfn[u] = dp[u] = ++dfscnt;
rdfn[dfscnt] = u;
siz[u] = 1;
son[u] = -1;
for(auto &v : g[u])
{
dfs1(v);
if(son[u] == -1 || siz[v] > siz[son[u]])
son[u] = v;
siz[u] += siz[v];
dp[u] = max(dp[u], dp[v]);
}
}
il void dfs2(int u, bool tag)
{
for(auto &v : g[u])
if(v != son[u])
dfs2(v, 0);
if(son[u] != -1)
dfs2(son[u], 1);
for(auto &v : g[u])
{
if(v != son[u])
{
for(int i = dfn[v];i <= dp[v];++i)
add(a[rdfn[i]]);
}
}
add(a[u]);
if(siz[u] % typ == 0 && c[siz[u] / typ] == typ)
ans++;
if(!tag)
{
for(int i = dfn[u];i <= dp[u];++i)
del(a[rdfn[i]]);
}
}
int main()
{
cin >> n;
for(int i = 1;i <= n;++i)
{
int x;
scanf("%d%d", &a[i], &x);
if(!x) continue;
g[x].pb(i);
}
c[0] = n;
dfs1(1);
dfs2(1, 0);
cout << ans;
return 0;
}