題目鏈接
http://codeforces.com/contest/791/problem/D
題意
給一個樹,節點數爲
思路
官方題解在這裏:http://codeforces.com/blog/entry/51068(當然如果我看的懂的話,現在就不寫博客了QAQ)
首先對於
如何求所有的偏置量呢?用樹形dp,對於每個節點u,我們分別計算以u爲根節點的子樹中,一定經過u的路徑的總偏置量之和,不斷遞歸下去,就可以求得所有偏置量。
對於偏置量的具體求法,我們記錄每個節點到root的距離,樹中任意一條路徑的長度
計數統計的部分很簡單,就不多說了,詳見代碼。
代碼
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define MS(x, y) memset(x, y, sizeof(x))
#define PB push_back
typedef long long LL;
const int MAXN = 2e5 +5;
vector<int> vec[MAXN];
int n, k;
LL ans;
LL cnt[MAXN][6], siz[MAXN];
void dfs(int u, int fa, int dep) {
cnt[u][dep % k] = siz[u] = 1;
int v, need;
for (int i = 0; i < vec[u].size(); ++i) {
v = vec[u][i];
if (v == fa) continue;
dfs(v, u, dep + 1);
for (int j = 0; j < k; ++j) for (int l = 0; l < k; ++l) {
need = ((k - (j + l - 2 * dep)) % k + k) % k;
ans += 1ll * need * cnt[u][j] * cnt[v][l];
}
for (int j = 0; j < k; ++j) cnt[u][j] += cnt[v][j];
siz[u] += siz[v];
}
ans += 1ll * siz[u] * (n - siz[u]);
}
int main() {
while (~scanf("%d%d", &n, &k)) {
for (int i = 1; i <= n; ++i) vec[i].clear();
MS(cnt, 0);
MS(siz, 0);
for (int i = 1, u, v; i < n; ++i) {
scanf("%d%d", &u, &v);
vec[u].PB(v);
vec[v].PB(u);
}
ans = 0;
dfs(1, 0, 0);
printf("%I64d\n", ans / k);
}
}