【算法】【树形dp】【换根dp】

8 阅读1分钟

树形dp(贡献法)

atcoder.jp/contests/ty…

#include <bits/stdc++.h>  
using namespace std;  
using ll=long long;  
const int M=1<<18;  
ll n;  
ll a[M],b[M];  
ll dp[M];  
vector<int>g[M];  
//计算每个节点的子树大小  
void dfs(int pos,int pre){  
    dp[pos]=1;  
    for(int to:g[pos]){  
        if(to==pre)continue;  
        dfs(to,pos);  
        dp[pos]+=dp[to];  
    }  
}  
void so(){  
      
}int main(){  
    cin>>n;  
    for(int i=1;i<=n-1;i++){  
        cin>>a[i]>>b[i];  
        g[a[i]].push_back(b[i]);  
        g[b[i]].push_back(a[i]);  
    }dfs(1,-1);  
    ll ans=0;  
    //切掉每一条边看贡献  
      
    for(int i=1;i<=n-1;i++){  
        //分成两部分  
        //s和n-s  
        //s取较小的那一块  
        //跨越这两部分的点对数量 = s × (n-s)  
          
    //    如果 a[i] 是 b[i] 的父节点:  
    //    dp[b[i]] = 子树大小(块 2 的大小)  
    //    dp[a[i]] = 整棵树的大小(n)  
        ll s=min(dp[a[i]],dp[b[i]]);  
        ans+=s*(n-s);  
    }cout<<ans;  
}

思路

见注释

换根dp

F - Distance Sums 2(模板)

#include <bits/stdc++.h>
using namespace std;

int main(){
    int n; cin >> n;
    vector<vector<int>> g(n);
    for(int i = 0; i < n - 1; i++) {
        int a, b; cin >> a >> b;
        g[a-1].push_back(b-1);
        g[b-1].push_back(a-1);
    }
    vector<long long> sub(n,1), ans(n);
    function<void(int, int, int)> dfs=[&](int i, int p, int d){
        ans[0] += d;
        for(int x : g[i]) if(x != p) {
            dfs(x, i, d+1);
            sub[i] += sub[x];
        }
    }; dfs(0, -1, 0);
    function<void(int, int)> dfs2=[&](int i, int p){
        for(int x : g[i]) if(x != p) {
            ans[x] = ans[i] - 2 * sub[x] + n;
            dfs2(x, i);
        }
    }; dfs2(0, -1);
    for(long long x : ans) cout << x << endl;
}

思路

ans[v]=ans[u]−size[v]+(N−size[v])