競プロ覚書

思考整理

競プロ典型90題 - 039 Tree Distance(★5)

問題 https://atcoder.jp/contests/typical90/tasks/typical90_am
解説 kyopro_educational_90/039.jpg at main · E869120/kyopro_educational_90 · GitHub

【問題】
重みのない木が与えられる。
任意の2頂点の最短パス長の総和を求めよ。


【典型ポイント】
主客転倒
辺をカットして頂点を2グループ化

【方針】
辺に注目し、各辺をカットして頂点を2グループに分けた時の頂点数の積が、その辺の「答えへの貢献度
貢献度の和が答えになる。
辺をカットしたときのそれぞれの頂点数は、[子ノードと、その子孫のノード数]をAとすると、A * (N - A)となる。

【感想】
知らんかった。主客転倒って何

int main(){
    int N; cin >> N;
    vector<vector<int>> G(N);
    rep(i, N-1){
        int a, b; cin >> a >> b;
        G[--a].push_back(--b);
        G[b].push_back(a);
    }

    vector<ll> vc(N); //各頂点の、その頂点を含む子孫の数
    auto dfs1 = [&](auto&& self, int v, int p) -> ll{
        vc[v] = 1;
        for(auto e:G[v]){
            if(e==p)continue;
            vc[v] += self(self, e, v);
        }
        return vc[v];
    };
    dfs1(dfs1, 0, -1);

    auto dfs2 = [&](auto&& self, int v, int p) -> ll{
        ll res = 0;
        for(auto e:G[v]){
            if(e==p)continue;
            res += self(self, e, v) + vc[e] * (N - vc[e]);
        }
        return res;
    };
    cout << dfs2(dfs2, 0, -1) << ln;
}