树上跳跃

题目描述
懒惰的温温今天上班也在偷懒。盯着窗外发呆的温温发现,透过窗户正巧能看到一棵n个节点的树。一棵n个节点的树包含n-1条边,且n个节点是联通的。树上两点之间的距离即两点之间的最短路径包含的边数。
温温瞧见树上有一只灵活的小松鼠正在节点间跳来跳去。观察了很长一段时间之后,温温发现:松鼠每次跳跃,从当前节点可以跳到任何与当前节点距离不超过k的节点。
突发奇想的温温想要知道,如果定义f(u, v)为从松鼠从u点到v点所需的最少跳跃次数,那么,对于树上的所有点对(u, v),f(u, v)的总和是多少。
注意:(u, v)和(v, u)视作同一个点对,只计算一次答案。

输入
第一行两个整数n和k。
接下来n-1行每行两个整数ai, bi,表示节点ai和bi之间存在一条边。

1 ≤ k ≤ 5
2 ≤ n ≤ 5000 for 40%
2 ≤ n ≤ 200000 for 100%

输出

输出一个整数,表示所求的f(u, v)总和。

样例输入 Copy
【样例1】

6 2
1 2
1 3
2 4
2 5
4 6

【样例2】

13 3
1 2
3 2
4 2
5 2
3 6
10 6
6 7
6 13
5 8
5 9
9 11
11 12

样例输出 Copy
【样例1】

20

【样例2】

114

#include <bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int N = 2e5 + 10 ;
int e[N << 1] , ne[N << 1] , h[N << 1]  , idx , flow[N] , in[N] , dp[N][6] , size[N] , k , b[6] , n ;
void add(int a, int b)
{
    e[idx] = b , ne[idx] = h[a] , h[a] = idx ++ ;
}
void dfs1(int u , int fa)
{
    memset(b , 0 , sizeof b) ;
    if(fa)
     {
       b[0] = dp[fa][0] - dp[u][k - 1] - size[u] ;
       for(int i = 1; i < k ;i ++) b[i] = dp[fa][i] - dp[u][i - 1] ;
     }
    dp[u][0] += b[k - 1] + n - size[u] ;
    for(int i = 1 ; i < k ;i ++) dp[u][i] += b[i - 1] ;
    for(int i = h[u] ; ~i ; i = ne[i])
    {
        int v = e[i] ;
        if(v == fa) continue ;
        dfs1(v , u) ;
    }
}
void dfs(int u , int fa)
{
    size[u] = 1 ;
    for(int i = h[u] ; ~i ; i = ne[i])
    {
        int v = e[i] ;
        if(v == fa) continue ;
        dfs(v , u) ;
        size[u] += size[v] ;
        dp[u][0] += dp[v][k - 1] + size[v] ;
        for(int i = 1; i < k ;i ++) dp[u][i] += dp[v][i - 1] ;
    }
    return ;
}
void work()
{
    cin >> n >> k ;
    memset(flow , 0 , sizeof flow) ;
    idx = 0 ;
    memset(h , -1 , sizeof h) ;
    for(int i = 1 , a , b , c ; i < n ;i ++)
        cin >> a >> b  ,
        add(a , b) , add(b , a);
    dfs(1, 0) ;
    dfs1(1 , 0) ;
    ll res = 0 ;
    for(int i = 1; i <= n ;i ++) res += 1ll * dp[i][0] ;
    cout << res / 2 << endl ;
    return ;
}
int main()
{
    work() ;
    return 0 ;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值