[JZOJ5515] 送你一朵圣诞树

Description

给出一棵树(不超过10个点可以作为根),每个点有一个权值a
要求按照某一个顺序选完所有点,一个点必须在父亲选完以后才能选,i号点在第j个被选对答案的贡献为a[i]*j
求最大的答案

Solution

考虑贪心

假如当前先选一个点是最优的,那么一定在选完它的父亲以后立即选它,那么可以将它与父亲缩在一个并查集里,并且答案加上父亲的联通块大小乘上儿子(可以是个联通块)的权值

考虑两个联通块先后的优劣
假设两个排序相邻的联通块x,y。他们的权值和分别是Sx,Sy,大小分别是Zx,Zy
那么x应当在y前面,当且仅当 ZxSy>ZySx
移项
SxZx<SyZy
按照这个来排序维护一个set就行了

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <set>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 30005
#define LL long long
using namespace std;
struct node
{
    LL w,sm,sz;
    node(LL _w,LL _sm,LL _sz):w(_w),sm(_sm),sz(_sz){};
    friend bool operator <(node x,node y)
    {
        return (x.sm*y.sz<y.sm*x.sz||(x.sm*y.sz==y.sm*x.sz&&x.w>y.w));
    }
};
int m,fa[N],f[N],n,rt[11],nt[2*N],fs[N],pr[N],dt[2*N];
LL sm[N],sz[N],ans;
set<node> h;
void link(int x,int y)
{
    nt[++m]=fs[x];
    dt[fs[x]=m]=y;
}
void dfs(int k,int ft)
{
    fa[k]=ft;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(p!=ft) dfs(p,k);
    }
}
int getf(int k)
{
     if(!f[k]||f[k]==k) return k;
     return f[k]=getf(f[k]);
}
int main()
{
    cin>>n;
    fo(i,1,n-1)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link(x,y),link(y,x);
    }
    fo(i,1,n)
    {
        int x;
        scanf("%d%d",&pr[i],&x);
        if(x) rt[++rt[0]]=i;
    }
    ans=0;
    fo(l,1,rt[0])
    {
        LL s1=0;
        dfs(rt[l],0);
        memset(f,0,sizeof(f));
        h.clear();
        fo(i,1,n) 
        {
            node cq=node(i,pr[i],1);
            h.insert(cq),sz[i]=1,sm[i]=pr[i],s1+=pr[i];
        }
        while(!h.empty())
        {
            node t=*h.begin();
            h.erase(h.begin());
            int p=t.w;
            if(p==rt[l]) continue;
            else
            {
                int fq=getf(fa[p]);
                s1+=sz[fq]*sm[p];
                node fp=node(fq,sm[fq],sz[fq]);
                h.erase(fp);
                sz[fq]+=sz[p],sm[fq]+=sm[p];
                sz[p]=sm[p]=0;
                f[p]=fq;
                h.insert(node(fq,sm[fq],sz[fq]));
            }
        }
        ans=max(ans,s1);
    }
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值