题解 P3047 [USACO12FEB]附近的牛Nearby Cows

Posted yuanqiqhfz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P3047 [USACO12FEB]附近的牛Nearby Cows相关的知识,希望对你有一定的参考价值。

题解 P3047 [USACO12FEB]附近的牛Nearby Cows

题目链接

题目大意是给你一颗树,对于每一个节点i,求出范围k之内的点权之和

看数据范围就知道暴力肯定是会t飞的,所以我们要考虑如何dp(代码习惯写dfs)

仔细思考一下我们发现点i走k步能到达的点分为以下两种

1.在i的子树中(由i点往下)

2.经过i的父亲(由i点往上)

这样的问题一般可以用两次dfs解决

我们定义状态f[i][j]表示i点往下j步范围内的点权之和,d[i][j]表示i点往上和往下走j步范围内点权之和。

第一次dfs我们求出所有的f[n][k],这个比较简单,对于节点u和其儿子v,f[u][k] += f[v][j - 1]就行了。(第一次dfs已知叶子节点推父亲节点)

第二次dfs我们通过已经求出的f数组推d数组,对于u和u的儿子v, d[v][k] += (d[u][k - 1] - f[v][k - 2]), 注意数组下表不要越界。d[i][j]的初始值应该赋为f[i][j],因为根节点的d[i][j]就是f[i][j]。(第二次dfs已知父亲节点推儿子节点)

思路说得可能有点罗嗦看代码吧:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
#define N 100005
using namespace std;
inline int read()

    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9')  if (c == '-') f = -1; c = getchar(); 
    while (c >= '0' && c <= '9')  x = x * 10 + c - '0'; c = getchar(); 
    return x * f;


ll f[N][25], d[N][25];
//f数组和d数组前面已经说了
int val[N];
int n, k, tot, head[N];

struct edge

    int to, next;
    edge() 
    edge(int x, int y)  to = x; next = y; 
a[N * 2];
//邻接表存图
void add(int from, int to)

    a[++tot] = edge(to, head[from]);
    head[from] = tot;


void dfs(int x, int fa)

    for (int i = 0; i <= k; i++) f[x][i] = val[x];
    for (int i = head[x]; i; i = a[i].next)
    
        int u = a[i].to;
        if (u != fa)
        
            dfs(u, x);//先dfs到叶子节点,然后推父亲节点
            for (int i = 1; i <= k; i++)
                f[x][i] += f[u][i - 1];
        
    

//第一次dfs
void dfs2(int x, int fa)

    for (int i = head[x]; i; i = a[i].next)
    
        int u = a[i].to;
        if (u != fa)
        
            d[u][1] += f[x][0];
            for (int i = 2; i <= k; i++)
                d[u][i] += d[x][i - 1] - f[u][i - 2];
            dfs2(u, x);//先dfs父亲节点,更新完儿子后dfs儿子
        
    

//第二次dfs
int main()

    n = read(), k = read();
    for (int i = 1; i < n; i++)
    
        int a1 = read(), a2 = read();
        add(a1, a2);
        add(a2, a1);
    
    for (int i = 1; i <= n; i++)
        val[i] = read();
    dfs(1, 1);
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= k; j++)
            d[i][j] = f[i][j];//把f赋给d
    dfs2(1, 1);
    for (int i = 1; i <= n; i++)
        cout << d[i][k] << endl;//输出答案
    return 0;

总之有一类树上dp的题,每个节点的答案与这个节点的父亲和儿子都有关,这时候我们可以分两次dfs求出答案,一般这两次dfs便利顺序不同。这要求大家对树的遍历有非常深的理解和实现能力,但是思路其实并没有那么难。

19.08.31

以上是关于题解 P3047 [USACO12FEB]附近的牛Nearby Cows的主要内容,如果未能解决你的问题,请参考以下文章

P3047 [USACO12FEB]附近的牛Nearby Cows

P3047 [USACO12FEB]附近的牛Nearby Cows 树形dp

洛谷P3047 [USACO12FEB]Nearby Cows(树形dp)

bzoj 1697: [Usaco2007 Feb]Cow Sorting牛排序

bzoj 1734: [Usaco2005 feb]Aggressive cows 愤怒的牛

BZOJ1734: [Usaco2005 feb]Aggressive cows 愤怒的牛