版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiang_6/article/details/83902440
CF:
dfs and similar dp trees *2000
题意:
给定一个n个点的树,(n最大2e5),如果原图(树)中有边 u-v, v-w ,那么现在你可以连一条边u-w;
问任意两点间最短距离的和;
思路:
开始想的对原树dfs,用dp[i][2] 分别表示到i结点距离为偶数和奇数的最小花费,但是很麻烦。。。
其实:按照题目给定的要求,我们发现原来两点间距离L,L为偶数-现在花费是L/2,L为奇数-现在花费是(L+1)/2;
正男则反??:相当于在题目给定的基础上减少了一些花费,如上; 其实大小一样
然后我们还知道可以O(n) 的求取树上(任意两点间距离)的总和sum;我们只要知道有多少个点对(设num个)间距离是奇数就好了,然后ans = (sum+num) / 2;
求距离为奇数的点对时,把树看作奇数层偶数层就好了,这些点对一定是奇数层和偶数层分别一个点;
ps 求树上任意两点间距离的和就是跑dfs的时候,用cnt[u]表示以u为根结点子树的大小(结点的个数),然后cnt[u] * (n-cnt[u])
相当于算边的贡献,这条边就是u跟父结点的连边;
#include<bits/stdc++.h>
using namespace std;
#define out fflush(stdout);
#define fast ios::sync_with_stdio(0),cin.tie(0);
#define FI first
#define SE second
typedef long long ll;
typedef pair<ll,ll> P;
const int maxn = 2e5 + 7;
const int INF = 0x3f3f3f3f;
const ll mod = 998244353;
ll n;
vector<int> vec[maxn];
ll ans = 0, num = 0;
ll cnt[maxn];
void dfs(int id, int f, int ce) {
if(ce) num++;
cnt[id] = 1;
for(auto i : vec[id]) {
if(i == f) continue;
dfs(i, id, ce^1);
cnt[id] += cnt[i];
}
ans += (cnt[id] * (n-cnt[id]));
}
int main() {
scanf("%lld", &n);
int u, v;
for(int i = 1; i < n; ++i) {
scanf("%d%d", &u, &v);
vec[u].push_back(v);
vec[v].push_back(u);
}
dfs(1, -1, 1);
ans = (ans + num*(n-num)) / 2LL;
printf("%lld\n", ans);
return 0;
}