BZOJ 1908. Pku2054 UVA1205 Color a Tree

传送门

看一眼感觉 $dp$,发现状态没法维护

考虑贪心,然后就想了两个错的贪心...

正解好神啊

首先如果权值最大的点能够一步染色那么肯定要染它

意思就是,一旦父节点被染色那么它就要接着被染色

那么把它们父子两合并成一个新的点,其他节点根据原来的边也连上来

考虑新的点的权值要怎么搞,现在既然这个节点包含了两个点,那么把它染色要两个单位时间,而染其他点只要 $1$ 单位时间

此时染它对整颗树产生的额外的代价为 $2$ 乘其他节点权值和,把其他点 $x$ 染色额外代价为 $1$ 乘其他节点 (非 $x$ 节点) 权值和

所以权值为原本节点权值之和除以 $2$,更大的情况也是同样处理,每次合并都计算此时合并的贡献

贡献就是父节点大小乘子节点权值和,因为要把子节点染色得先把父节点染好,此时子节点会产生额外的代价

这样一直合并最后只剩下根节点时答案就出来了,具体看代码吧

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e5+7;
int n,rt,val[N],sz[N],fa[N],ans;
inline void clr() { for(int i=1;i<=n;i++) fa[i]=val[i]=sz[i]=0; ans=0; }
void solve()
{
    for(int i=1;i<n;i++)
    {
        int x=0,p;
        for(int j=1;j<=n;j++)
            if(j!=rt && 1.0*val[x]/sz[x]<1.0*val[j]/sz[j]) x=j;//找到权值最大的点
        p=fa[x]; ans+=sz[p]*val[x];//我们这里算的是额外的代价,不包括把本身染色的代价
        sz[p]+=sz[x]; val[p]+=val[x];
        val[x]=0; fa[x]=p;//合并
        for(int j=1;j<=n;j++)
            if(fa[j]==x) fa[j]=p;//其他点按原来关系连上来
    }
}
int main()
{
    sz[0]=1;
    while(233)
    {
        n=read(),rt=read(); int a,b;
        if(!n&&!rt) break;
        for(int i=1;i<=n;i++)
            val[i]=read(),ans+=val[i],sz[i]=1;//ans初始为所有点染色本身的代价
        for(int i=1;i<n;i++) { a=read(),b=read(); fa[b]=a; }
        solve();
        printf("%d\n",ans);
        clr();
    }
    return 0;
}

 

posted @ 2019-08-25 16:22  LLTYYC  阅读(180)  评论(0编辑  收藏  举报