1. 程式人生 > 實用技巧 >CF 1467E Distinctive Roots in a Tree

CF 1467E Distinctive Roots in a Tree

  • 給出一棵樹,結點有點權,求出樹上有多少個結點滿足從它開始的任何一條路徑點權不重複。

考慮每對權值相同的點,只有在它們之間範圍的點是可能成為答案的。

如果暴力列舉點對,即使可以 \(O(1)\) 打標記,複雜度也是 \(O(n^2)\) 的,考慮優化。

先將樹轉化為有根樹,對於一個點來說,

它處在的點對中另外一個節點可能在它的某棵子樹中或整個子樹外。

若在其子樹中,則需要對全域性除了該子樹打上標記。

否則,需要對整棵子樹打上那個標記。

這樣的話,每個結點最多修改兒子個數 + \(1\) 次,複雜度是線性的。

可以通過 \(dfs\) 序上差分來實現子樹修改。

可以在 \(dfs\) 的過程中開一個桶記錄每種顏色出現次數,前後做差即可得到子樹內部該種顏色的數目。

#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define fi first
#define se second
using namespace std;

typedef long long LL;
typedef pair <int, int> P;
const int inf = 0x3f3f3f3f, N = 3e5 + 10;
template <typename T>
void rd_(T &x) {
    x = 0; int f = 1;
    char ch = getchar();
    for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x*10 + ch - '0';
    x *= f;
}

int n, h[N], cnt, a[N], tot, tim, ans, b[N], c[N], st[N], d[N], ed[N];
map <int, int> M;
struct Edge {
    int to, next;
} e[N<<1];

void add_(int u, int v) {
    e[++cnt] = (Edge) {v, h[u]};
    h[u] = cnt;
}

void dfs_(int u, int fa = 0) {
    st[u] = ++tim;
    int last = b[a[u]], x;
    b[a[u]]++;
    for (int v, i = h[u]; i; i = e[i].next) {
        v = e[i].to;
        if (v == fa) continue;
        x = b[a[u]];
        dfs_(v, u);
        if (x != b[a[u]]) 
            d[1]++, d[st[v]]--, d[ed[v] + 1]++;
    }
    ed[u] = tim;
    if (b[a[u]] - last != c[a[u]]) 
        d[st[u]]++, d[ed[u] + 1]--;
}

int main() {
    rd_(n);
    rep (i, 1, n) {
        rd_(a[i]);
        if (!M[a[i]]) M[a[i]] = ++tot;
        a[i] = M[a[i]], c[a[i]]++;
    }
    for (int u, v, i = 1; i < n; i++) {
        rd_(u), rd_(v);
        add_(u, v), add_(v, u);
    }
    dfs_(1);
    rep (i, 1, n) d[i] += d[i - 1], ans += d[i] == 0;
    printf("%d\n", ans);
}