[JZOJ5515] 送你一朵聖誕樹
阿新 • • 發佈:2019-02-18
Description
給出一棵樹(不超過10個點可以作為根),每個點有一個權值a
要求按照某一個順序選完所有點,一個點必須在父親選完以後才能選,i號點在第j個被選對答案的貢獻為a[i]*j
求最大的答案
Solution
考慮貪心
假如當前先選一個點是最優的,那麼一定在選完它的父親以後立即選它,那麼可以將它與父親縮在一個並查集裡,並且答案加上父親的聯通塊大小乘上兒子(可以是個聯通塊)的權值
考慮兩個聯通塊先後的優劣
假設兩個排序相鄰的聯通塊x,y。他們的權值和分別是Sx,Sy,大小分別是Zx,Zy
那麼x應當在y前面,當且僅當
移項
x<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);
}