mayoko’s diary

プロコンとかいろいろ。

Educational Codeforces Round 2 E. Lomsat gelral

解法

"データ構造をマージする一般的なテク" を使います。なにそれ, という人は以下のページをご覧ください。
topcoder.g.hatena.ne.jp

今回の問題の場合, M[v][color] = (頂点 v 以下の部分木で, 色が color の頂点がいくつあるか) というのを, 正直に更新していくと TLE しそうです。ですが, 頂点 v と頂点 u の情報をマージするときに, M[v].size() と M[u].size() のうち, 大きいサイズの方に情報をマージするようにすると, 各色の情報の移動回数がたかだか log N 回になるので, 全体計算量は O(N log N) で収まります。

他に, maxi[v] = (v 以下の部分木で支配的な色の個数), dSum[v] = (v 以下の部分木で支配的な色の数の和(C[0] + C[4] + ... みたいな)) というのを用意しておくと, 更新処理が高速に出来ます。

const int MAXN = 100100;
int C[MAXN], maxi[MAXN];
vector<int> G[MAXN];
ll ans[MAXN], dSum[MAXN];
map<int, int> M[MAXN];

void merge(int v, int u) {
    if (M[v].size() < M[u].size()) {
        swap(M[v], M[u]);
        swap(dSum[v], dSum[u]);
        swap(maxi[v], maxi[u]);
    }
    for (auto p : M[u]) {
        M[v][p.first] += p.second;
        if (maxi[v] < M[v][p.first]) {
            dSum[v] = 0;
            maxi[v] = M[v][p.first];
        }
        if (M[v][p.first] == maxi[v]) {
            dSum[v] += p.first;
        }
    }
}

void dfs(int v, int p) {
    for (int ch : G[v]) {
        if (ch == p) continue;
        dfs(ch, v);
        merge(v, ch);
    }
    M[v][C[v]] += 1;
    if (M[v][C[v]] > maxi[v]) {
        dSum[v] = 0;
        maxi[v] = M[v][C[v]];
    }
    if (M[v][C[v]] == maxi[v]) dSum[v] += C[v];
    ans[v] = dSum[v];
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> C[i];
    for (int i = 0; i < n-1; i++) {
        int x, y;
        cin >> x >> y;
        x--; y--;
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs(0, -1);
    for (int i = 0; i < n; i++) {
        cout << ans[i] << " ";
    }
    cout << endl;
    return 0;
}