• P3047 [USACO12FEB]附近的牛Nearby Cows


      我呦呦呦又回来啦!

      再来发一篇树形dp~

      昨晚看的这道题,想了好久也不会(然后我就去睡觉了),今天突然明白是怎么回事了,一次A!(好感动)

      简单分析一下,首先如果你想拿部分分,那是超级简单的,每一个点都dfs一遍咯,说实话真没啥技术含量。

      考虑:假设我已经知道了一个点分别到它周围从1~k的距离的牛的数目的话,并且它的儿子节点我们已经求出这个节点以上(包括这个点)分别到它周围从1~k的距离的牛的数目,我就可以用父亲去更新儿子(以及简单的容斥原理)。

      那为什么对于儿子节点,为什么我们不去考虑他父亲的父亲的值呢?

      首先,对于这次dfs,一定是用父亲去更新儿子的,那么在儿子上面的点已经是不用更新的了,就考虑父亲节点的分支问题。那么这些点如果到儿子距离为 j ,它们到父亲的距离就一定为 j - 1 。但是我们加上的不仅仅是儿子下面的,也包括了它父亲上面的点,而这些点和已知的儿子上面(包括儿子)的部分有重合,那我们只需用一下可爱的容斥原理,减掉这部分就好 >\<,那么因为每一次只向上推进一个点,下面只要补足一个点作为用来更新的元素就可以了,每一次维护的一定都是最优解(实在不行:根节点本身就是最优;他的儿子只能用它更新,最优;他儿子的儿子只需要用到现有的最优解,就是它的父亲……),所以只需要用他它的父亲去更新。

      那么提前处理的跑一遍dfs,用父亲更新儿子再跑一遍dfs,就大功告成啦!

      另外有一点需要注意,循环的时候不一定大到小和小到大都可以,如果更新这个元素需要用到更小的还没有被更新过的原始解,那么只能从大到小枚举。

      下面上代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    #define maxn 200005
    int head[maxn],to[maxn],nxt[maxn],c[maxn],f[maxn][25];
    int n,k,cnt;
    void add(int a,int b)
    {
        to[++cnt]=b;
        nxt[cnt]=head[a];
        head[a]=cnt;
    }
    void dfs(int u,int fa)
    {
        f[u][0]=c[u];
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(v==fa) continue;
            dfs(v,u);
            for(int j=1;j<=k;j++)
            f[u][j]+=f[v][j-1];
        }
    }
    void dp(int u,int fa)
    {
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(v==fa) continue;
            for(int j=k;j>=2;j--)
                f[v][j]-=f[v][j-2];
            for(int j=k;j>=1;j--)
                f[v][j]+=f[u][j-1];
            dp(v,u); 
        }
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b); 
            add(a,b);
            add(b,a);
        }
        for(int i=1;i<=n;i++)
            scanf("%d",&c[i]);
        dfs(1,0); 
        dp(1,0);
        for(int i=1;i<=n;i++)
        {
            int ans=0;
            for(int j=0;j<=k;j++)
                ans+=f[i][j];
            printf("%d
    ",ans);
        }
        return 0;
    }
     
  • 相关阅读:
    JAVA实现的异步redisclient
    权限表设计
    操作系统之存储管理(续)
    leetcode第一刷_Jump Game
    linux之stat函数解析
    重温微积分 —— 偏微分与链式法则
    重温微积分 —— 偏微分与链式法则
    所谓敏感(数字的敏感)
    所谓敏感(数字的敏感)
    推理集 —— 特殊的工具
  • 原文地址:https://www.cnblogs.com/popo-black-cat/p/10217708.html
Copyright © 2020-2023  润新知