[USACO12FEB Gold]Nearby Cows 解题报告

[USACO12FEB Gold]Nearby Cows

问题描述

给你一棵 n n n 个点的树,点带权,对于每个节点求出距离它不超过 k k k 的所有节点权值和 m i m_i mi

输入格式

第一行两个正整数 n n n k k k

接下来 $ n−1$ 行,每行两个正整数 u u u v v v,表示 u u u v v v 之间有一条边。

最后 $ n$ 行,每行一个非负整数  c i c_i ci ,表示点权。

输出格式

输出 n n n 行,第 $ i$ 行一个整数表示 m i m_i mi

样例输入1
6 2  
5 1  
3 6  
2 4  
2 1  
3 2  
1  
2  
3  
4  
5  
6
样例输出1
15  
21  
16  
10  
8  
11
数据范围

对于 100 100 100%的数据: 1 ≤ n ≤ 1 0 5 , 1 ≤ k ≤ 20 , 0 ≤ c i ≤ 1000 1≤n≤10^5 ,1≤k≤20,0≤ci≤1000 1n1051k200ci1000

求距离它不超过k的所有节点权值的和

首先想的是一个一个枚举

不知道数据允许不

啊,不可以(ˉ▽ˉ;)…

所以,我们来想一想另外的方法

???

万能的DP出场了!!!

我们可以尝试

d [ i ] [ j ] d [i] [j] d[i][j]表示从 i i i点开始向下 j j j内权值之和

f [ i ] [ j ] f[i] [j] f[i][j]代表从 i i i点开始距离 ≤ j ≤j j的权值之和

d [ i ] [ j ] d[i] [j] d[i][j]可以很容易算出来

对于一个点u, d [ u ] [ j ] = C u + ∑ v ∈ s o n u d [ v ] [ j − 1 ] d[u][j]=C_{u}+∑_{v∈son_{u}}d[v][j-1] d[u][j]=Cu+vsonud[v][j1]

f [ i ] [ j ] f[i] [j] f[i][j]也可以得出, f [ u ] [ j ] = f [ f a [ u ] ] [ j − 1 ] − d [ u ] [ j − 2 ] + d [ u ] [ j ] f[u][j]=f[fa[u]][j-1]-d[u][j-2]+d[u][j] f[u][j]=f[fa[u]][j1]d[u][j2]+d[u][j]

这个方程式说父亲的信息+u向下的信息-无用信息

这里无用的信息就是 d [ u ] [ j − 2 ] d[u][j-2] d[u][j2]

因为父亲的信息包括了从父亲往下的信息

而对于 u u u来说,父亲往下 j − 1 j-1 j1的点距离ta并没有 j j j的距离

只有 j − 2 j-2 j2的距离

所以需要将父亲往下的答案减去

所以 f [ f a [ u ] ] [ j − 1 ] − d [ u ] [ j − 2 ] f[fa[u]][j-1]-d[u][j-2] f[fa[u]][j1]d[u][j2]就是 u u u往上 j j j的权值之和

最后输出 f [ i ] [ k ] f[i] [k] f[i][k]就行了

code

#include<bits/stdc++.h>
using namespace std;
const int N=100002;
int n,k,cnt=0,x,y;
int hd[N],c[N],d[N][22],f[N][22];
struct Node {
    int nxt,to;
} tr[N*2];
void dfs(int x,int fa,int u) {
    d[x][u]+=c[x];
    for(int i=hd[x]; i; i=tr[i].nxt) {
        if(tr[i].to==fa) continue;
        d[x][u]+=d[tr[i].to][u-1];
        dfs(tr[i].to,x,u);
    }
}
void dfsb(int x,int fa,int u) {
    int w;
    if(u>=2) w=d[x][u-2];
    else w=0;
    f[x][u]=f[fa][u-1]-w+d[x][u];
    for(int i=hd[x]; i; i=tr[i].nxt){
        if(tr[i].to!=fa){
            dfsb(tr[i].to,x,u);
        }
    }
         
}
void add(int x,int y) {
    tr[++cnt].nxt=hd[x];
    tr[cnt].to=y;
    hd[x]=cnt;
}
int main() {
    scanf("%d%d",&n,&k);
    for(int i=1; i<n; i++) {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for(int i=1; i<=n; i++) {
        scanf("%d",&c[i]);
        d[i][0]=f[i][0]=c[i];
    }
    if(!k) {
        for(int i=1; i<=n; i++) {
            printf("%d\n",c[i]);
        }
        return 0;
    }
    for(int i=1; i<=k; i++) {
        dfs(1,0,i);
        f[1][i]=d[1][i];
    }
    for(int i=1; i<=k; i++) {
        for(int j=hd[1];j;j=tr[j].nxt){
            dfsb(tr[j].to,1,i);
        }
    }
    for(int i=1; i<=n; i++) {
        printf("%d\n",f[i][k]);
    }
    return 0;
}

完结撒花❀

★,°:.☆( ̄▽ ̄)/$:.°★ 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值