思路:讲真,这玩意我看了题解也看了一下午才懂。。智商太低。题解的思路是这样的,首先当k=1时,算所有边经过的次数和就是答案。这个应该很容易懂吧不解释了。计算方法也很简单,基础树形dp而已。麻烦的来了。k>1时,计算方法是这样的,例如两点之间隔了10条边,你每次能跳3条边,那么当你跳了3次之后就跳了9条边,剩下一次虽然你能跳3条边,但是也只差一条边的距离,从此我们能想到把缺的边数补上2,用12/3就知道这两点的距离是多少了。也就是说,我们接下来需要计算的是两两点之间还差多少步能被k整除,然后累加起来,把结果/k就是答案了。然后问题就转移到了怎么求两两之间的步长,我们跑dfs的时候记录每个点的深度,然后两重循环枚举深度,任意两点步长等于depth[x]+depth[y]-depth[z]*2,z为两点最近的公共祖先的深度,那么这道题就能解出来了。下面给代码:
#include<iostream>
#include<cmath>
#include<queue>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<utility>
#include<map>
#include<vector>
#define maxn 200005
#define inf 0x3f3f3f3f
using namespace std;
typedef long long LL;
const double eps = 1e-8;
struct node{
int v, next;
}p[maxn << 1];
int head[maxn], n, k;
LL dp[maxn][10], size[maxn], ans;
void dfs(int x, int fa,int depth){
size[x] = dp[x][depth%k] = 1;
for (int i = head[x]; ~i; i = p[i].next){
if (p[i].v == fa)
continue;
dfs(p[i].v, x, depth + 1);
for (int a = 0; a < k; a++){
for (int b = 0; b < k; b++){
int dis = ((a + b) % k - (depth * 2) % k + k) % k;
ans += ((k - dis) % k)*dp[x][a] * dp[p[i].v][b];
}
}
for (int a = 0; a < k; a++){
dp[x][a] += dp[p[i].v][a];
}
ans += size[p[i].v] * (n - size[p[i].v]);
size[x] += size[p[i].v];
}
}
int main(){
scanf("%d%d", &n, &k);
memset(head, -1, sizeof(head));
int len = 0;
for (int i = 0; i < n - 1; i++){
int u, v;
scanf("%d%d", &u, &v);
p[len].v = v;
p[len].next = head[u];
head[u] = len++;
p[len].v = u;
p[len].next = head[v];
head[v] = len++;
}
dfs(1, 0, 0);
printf("%lld\n", ans / k);
}