codeforces790B

来源:互联网 发布:加湿器 类型 知乎 编辑:程序博客网 时间:2024/05/22 02:27

题面在这里

题目大意:

有一棵树,在树上每一步 可以跳k条边。设f(s, t)表示从s到t的最小步数。求所有点对的f值之和。


先考虑k = 1的情况,那么就是求树上任意两点的距离之和,相当于求每一条边被经过的次数,答案应该就是任意一条边左右两边点数的乘积之和。

然后如果k != 1,那么在一条路径长度不被k整除的时候就会出现问题,因为有一个余数,这个余数我们需要再走一步。

处理这个余数的问题,我们转化一下,可以先把这个长度补上一个数使得被k整除,然后我们就按照k = 1来计算答案,最后答案再除以k就行。

具体转移的时候,我们可以把u看做两个点的最近公共祖先,枚举一下子树内节点的深度来计算。

记f[i][j]表示以i为根的子树内深度%k = j的节点的个数,sz[i]表示i子树的大小,ans记录答案,然后转移代码大致如下,其中v是u的儿子,depth是u的深度:

rep(a, 0, K-1)rep(b, 0, K-1){int dis = ((a+b)%K - 2*depth%K + K)%K;ans += ((K-dis)%K)*f[u][a]*f[v][b];}

完整代码:

/*************************************************************Problem: codeforces 790B - Bear and Tree JumpsUser: fengyuanLanguage: C++Result: AcceptedTime: 187 msMemory: 30200 KBSubmit_Time: 2017-11-17 21:43:43*************************************************************/#include<cstdio>#include<cstring>#include<iostream>#define rep(i, x, y) for (int i = (x); i <= (y); i ++)#define down(i, x, y) for (int i = (x); i >= (y); i --)using namespace std;typedef long long LL;const int N = 200010;LL ans = 0;int n, K, cnt = 0;int head[N];LL f[N][10], sz[N];struct Edge{int to, nex;}e[N<<1];inline void add(int x, int y){e[++ cnt].to = y;e[cnt].nex = head[x];head[x] = cnt;}inline void dfs(int u, int fa, int depth){sz[u] = f[u][depth%K] = 1;for (int i = head[u]; i; i = e[i].nex){int v = e[i].to;if (v == fa) continue;dfs(v, u, (depth+1)%K);rep(a, 0, K-1)rep(b, 0, K-1){int dis = ((a+b)%K - 2*depth%K + K)%K;ans += ((K-dis)%K)*f[u][a]*f[v][b];}rep(j, 0, K-1) f[u][j] += f[v][j];ans += sz[v]*(n - sz[v]);sz[u] += sz[v];}}int main(){scanf("%d%d", &n, &K);rep(i, 1, n-1){int x, y; scanf("%d%d", &x, &y);add(x, y); add(y, x);}dfs(1, 0, 0);cout << ans / K << endl;return 0;}