F. Strange Housing
题意
给一棵 n n n 个节点的树,每个节点都有一个权值。统计有多少个满足以下要求的节点:从这个点出发的任意路径中,没有两个相同的权值出现在同一路径。
题解
很难直接求出满足要求的节点,但是我们可以标记不符合要求的点。
- 如果遍历到点 u u u ,并且在 a [ u ] a[u] a[u] 没有全部出现在以 u u u 为根的子树中,那么 u u u 的子树都不满足,因为一定有一条路径经过 u u u 和另一个权值等于 a [ u ] a[u] a[u] 的点。
- 如果遍历到点 u u u ,并且在以 u u u 为根的子树中也有一个点 v v v 的权值为 a [ v ] = a [ u ] a[v]=a[u] a[v]=a[u] ,那么那个 v v v 的子树不满足,并且除了包含点 v v v 的 u u u 的子树,其他点也都不可取。
- 把权值离散化然后树上差分即可。
代码
#pragma region
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 2e5 + 5;
int n, a[maxn], b[maxn], c[maxn];
vector<int> g[maxn];
int dfn[maxn], nowid = 0, sz[maxn];
int sum[maxn], cnt[maxn];
void upd(int l, int r, int val) {
if (l > r) return;
c[l] += val, c[r + 1] -= val;
}
void dfs(int u, int f) {
dfn[u] = ++nowid, sz[u] = 1;
int tmp1 = cnt[a[u]];
++cnt[a[u]];
for (auto v : g[u]) {
if (v == f) continue;
int tmp2 = cnt[a[u]];
dfs(v, u);
sz[u] += sz[v];
if (cnt[a[u]] > tmp2) {
upd(1, dfn[v] - 1, 1);
upd(dfn[v] + sz[v], n, 1);
}
}
tmp1 = cnt[a[u]] - tmp1;
if (tmp1 < sum[a[u]]) upd(dfn[u], dfn[u] + sz[u] - 1, 1);
}
int main() {
scanf("%d", &n);
rep(i, 1, n) scanf("%d", &a[i]), b[i] = a[i];
sort(b + 1, b + 1 + n);
rep(i, 1, n) {
a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;
sum[a[i]]++;
}
rep(i, 1, n - 1) {
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
int ans = 0;
rep(i, 1, n) {
c[i] += c[i - 1];
if (!c[i]) ++ans;
}
printf("%d\n", ans);
}