树形DP【洛谷P3047】 [USACO12FEB]附近的牛Nearby Cows

本文解决了一个复杂的算法问题,即USACO竞赛中的“附近的牛”问题。通过对农场布局的深入分析,利用深度优先搜索和动态规划技术,成功计算了每块田野上需要种植的草量,以满足所有奶牛的需求。文章详细介绍了算法的设计思路和实现代码。

P3047 [USACO12FEB]附近的牛Nearby Cows

农民约翰已经注意到他的奶牛经常在附近的田野之间移动。考虑到这一点,他想在每一块土地上种上足够的草,不仅是为了最初在这片土地上的奶牛,而且是为了从附近的田地里去吃草的奶牛。

具体来说,FJ的农场由N块田野构成(1 <= n <= 100,000),每两块田野之间有一条无向边连接(总共n-1条边)。FJ设计了农场,任何两个田野i和j之间,有且只有一条路径连接i和j。第 i块田野是C(i)头牛的住所,尽管奶牛们有时会通过k条路到达其他不同的田野(1<=k<=20)。

FJ想在每块田野上种上够M(i)头奶牛吃的草。M(i)指能从其他点经过最多k步就能到达这个点的奶牛的个数。

现给出FJ的每一个田野的奶牛的数目,请帮助FJ计算每一块田野的M(i)。

先处理出来子树内的。

\(f(i)(j)\)表示以i为根的子树内距离i为j的答案。

很简单的转移:

\(f(u)(j)=\sum_{f(v)(j-1)}​\)

然后转移出非子树内的,为了解决DP后效性要开一个中间量数组转移一下。

code:

#include <iostream>
#include <cstdio>

#define int long long

using namespace std;

const int wx=100017;

inline int read(){
    int sum=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
    return sum*f;
}

int f[wx][27],g[wx][27];
int head[wx],val[wx];
int n,k,num;

struct e{
    int nxt,to;
}edge[wx*2];

void add(int from,int to){
    edge[++num].nxt=head[from];
    edge[num].to=to;
    head[from]=num;
}

void dfs(int u,int fa){
    f[u][0]=val[u];
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==fa)continue;
        dfs(v,u);
        for(int j=1;j<=k;j++){
            f[u][j]=(f[u][j]+f[v][j-1]);
        }
    }
}

void dp(int u,int fa){
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==fa)continue;
        g[v][1]+=f[u][0];
        for(int j=2;j<=k;j++){
            g[v][j]+=(f[u][j-1]-f[v][j-2]);
        }
        for(int j=1;j<=k;j++)f[v][j]+=g[v][j];
        dp(v,u);
    }
}

signed main(){
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    n=read(); k=read();
    for(int i=1;i<n;i++){
        int x,y;
        x=read(); y=read();
        add(x,y); add(y,x);
    }
    for(int i=1;i<=n;i++)val[i]=read();
    dfs(1,0); dp(1,0);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=k;j++){
            f[i][j]+=f[i][j-1];
        }
    }
    for(int i=1;i<=n;i++)printf("%lld\n",f[i][k]);
    
    return 0;
}

转载于:https://www.cnblogs.com/wangxiaodai/p/9877457.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值