树形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])