codeforces790B - Bear and Tree Jumps

20 篇文章 0 订阅
20 篇文章 0 订阅

题面在这里

题目大意:

有一棵树,在树上每一步 可以跳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 Jumps
	User: fengyuan
	Language: C++
	Result: Accepted
	Time: 187 ms
	Memory: 30200 KB
	Submit_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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值