[POI2014]FAR-FarmCraft

传送门

preface

  下午颓的时候看到这道题,当时也没怎么想,就去找题解,结果听不懂ta在讲啥。晚上自己回来弄一弄就完了,就写个题解翻译一下。

分析

  这个树形dp就没什么好说的了。用dp[i]表示把以i为根的子树全处理完后最少还要多少时间(划重点)。刚开始想最常规的思路,提交一发只对了一个点,然后才改用题解的dp。这样的话,最后只用判断c1和dp[1]哪个大再加上所有边的两倍就可以了。

  考虑转移。贪心的想,我们每到一个节点就开始安装,然后走这个节点的儿子里dp值最大的那个,显然,这样走可以保证不比其他方式劣。所以把i的儿子用一个vector全部记录下来,按照dp值从大到小排序。因为dp[i]受i的儿子中最大dp值限制,所以dp[i]就是每个儿子的dp值减去按照贪心策略遍历每个儿子的时间的最大值。再把处理后的dp[i]和ci-(sizi-1)*2取max就可以了。

  p.s.这题也有省选难度?

code

#include<bits/stdc++.h>
#define ll long long
#define maxn 500010
#define reg register
using namespace std;
int n,w[maxn],head[maxn],k,dp[maxn],dep[maxn],siz[maxn];
struct node
{
    int to,nxt;
} edge[maxn*2];
void add(int u,int v)
{
    edge[++k].nxt=head[u];
    edge[k].to=v;
    head[u]=k;
}
bool cmp(int x,int y)
{
    return dp[x]>dp[y];
}
void dfs(int u,int fa)
{
    siz[u]=1;
    vector<int> tmp;
    for(int i=head[u]; i; i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v!=fa)
        {
            tmp.push_back(v);
            dep[v]=dep[u]+1;
            dfs(v,u);
            siz[u]+=siz[v];
        }
    }
    if(u!=1) dp[u]=w[u]-(siz[u]-1)*2;
    int sum=(siz[u]-1)*2;
    sort(tmp.begin(),tmp.end(),cmp);
    for(int i=0,sz=tmp.size(); i<sz; i++)
    {
        sum-=siz[tmp[i]]*2;
        dp[u]=max(dp[u],dp[tmp[i]]-sum-1);
    }
    dp[u]=max(dp[u],0);
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++) scanf("%d",&w[i]);
    int x,y;
    for(int i=1; i<n; i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1,1);
    printf("%d\n",max(dp[1],w[1])+(n-1)*2);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wCTSd/p/9885723.html