[Usaco 2012 Feb]Nearby Cows

Description

Farmer John has noticed that his cows often move between nearby fields. Taking this into account, he wants to plant enough grass in each of his fields not only for the cows situated initially in that field, but also for cows visiting from nearby fields. Specifically, FJ’s farm consists of N fields (1 <= N <= 100,000), where some pairs of fields are connected with bi-directional trails (N-1 of them in total). FJ has designed the farm so that between any two fields i and j, there is a unique path made up of trails connecting between i and j. Field i is home to C(i) cows, although cows sometimes move to a different field by crossing up to K trails (1 <= K <= 20). FJ wants to plant enough grass in each field i to feed the maximum number of cows, M(i), that could possibly end up in that field – that is, the number of cows that can potentially reach field i by following at most K trails. Given the structure of FJ’s farm and the value of C(i) for each field i, please help FJ compute M(i) for every field i.
FJ发现他的牛经常跑到附近的草地去吃草,FJ准备给每个草地种足够的草供这个草地以及附近草地的奶牛来吃。FJ有N个草地(1<=N<=100000),有N-1条双向道路连接这些草地,FJ精心设计了这些道路使每两个草地有且仅有一条简单路径连接。第i个草场有Ci头牛,有时候奶牛会走过K条道路到其他草地吃草。FJ想知道每个草场最多可能有的奶牛数量Mi,即所有走过K条道路后可能到达i的奶牛总数。

Input

  • Line 1: Two space-separated integers, N and K.
  • Lines 2…N: Each line contains two space-separated integers, i and j (1 <= i,j <= N) indicating that fields i and j are directly connected by a trail.
  • Lines N+1…2N:
    *Line N+i contains the integer C(i). (0 <= C(i) <= 1000)
    Output
  • Lines 1…N: Line i should contain the value of M(i).
    Sample Input
    6 2
    5 1
    3 6
    2 4
    2 1
    3 2
    1
    2
    3
    4
    5
    6

Sample Output

15
21
16
10
8
11

题目解析:
题面的意思是有一棵节点数为n的双向边的数,并且给你每一个节点的值,现从i出发,问在k步范围内能收集到多少值。

思路解析:
首先想到的办法肯定是枚举每一个点,以这个点为根,向下寻找k层,但这样暴力枚举的话会走许多重复的路,肯定会超时,那么最好的方法就是dp了。
一开始,我们总体把dp分为两块,当前i上面的为一块,下面的为一块;
下面的一块:
这个很好求,一路求下去就可以了,设当前点为num[ i ][ j ],子节点为son[ i ],则表示从节点 i 向下走 j 层可到达的值,则num[ i ][ j ]+=num[ son[ i ] ][ j-1 ];
上面的一块:
可以分为两块,单纯的加父节点,和除父亲点外所有上面节点的值(也可以说是你另外分支的值)
用样例来说,假设当前点为3,则下面一块的值为6的值,而上面的一块,前一类(只包括父节点)的有1的值和2的值,,后一类则是,4的值和6的值。
搞清楚这个后再回到程序上来,mum[ i ][ j ]为第一类的dp数组,与num相反,恰好是
mum[ son[ i ] ] [ j ]+=mum[ i ][ j-1 ];而flag为第二类的dp数组,dp公式为
flag[ son[ i ] ][ j ]+=num[ i ][ j-1 ]-num[ son[ i ] ][ j-2 ]+flag[ i ][ j-1 ];
首先将i的上面在j-1范围内所有值现加起来,再将以i为父节点的子分支除son[ i ]所在这一分支外的值相加。
最后将3个数组相加,得到终值。
代码实现

#include<bits/stdc++.h>
using namespace std;
int head[210000],son[210000],pre[210000];
int tot,k,n;
int num[110005][21],mum[110005][21],flag[110005][21];
bool f[110005];
void tree(int a,int b)
{
    pre[++tot]=head[a];
    head[a]=tot;
    son[tot]=b;
}
void dp(int u,int h)
{
    f[u]=1;
    for(int i=head[u];i;i=pre[i])
    {
        if(!f[son[i]])
        {
            if(h==0)
            for(int j=1;j<=k;j++)
            mum[son[i]][j]+=mum[u][j-1];
            if(h==1)
            for(int j=2;j<=k;j++)
            {
                flag[son[i]][j]=flag[u][j-1];
                flag[son[i]][j]+=num[u][j-1]-num[son[i]][j-2];
            }
            dp(son[i],h);
            for(int j=1;j<=k;j++)
            if(h==0)
            num[u][j]+=num[son[i]][j-1];
        }
    }
}
int main()
{
    int head,tail;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&head,&tail);
        tree(head,tail);
        tree(tail,head);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i][0]);
        mum[i][0]=num[i][0];
    }
    dp(1,0);
    memset(f,0,sizeof(f));
    dp(1,1);
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
    {
        int ans=num[i][0];
        for(int j=1;j<=k;j++)
        ans+=num[i][j]+flag[i][j]+mum[i][j];
        printf("%d\n",ans);
    }
    return 0;
}

如有不当之处,还请谅解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值