1. 程式人生 > >[JZOJ5515] 送你一朵聖誕樹

[JZOJ5515] 送你一朵聖誕樹

Description

給出一棵樹(不超過10個點可以作為根),每個點有一個權值a
要求按照某一個順序選完所有點,一個點必須在父親選完以後才能選,i號點在第j個被選對答案的貢獻為a[i]*j
求最大的答案

Solution

考慮貪心

假如當前先選一個點是最優的,那麼一定在選完它的父親以後立即選它,那麼可以將它與父親縮在一個並查集裡,並且答案加上父親的聯通塊大小乘上兒子(可以是個聯通塊)的權值

考慮兩個聯通塊先後的優劣
假設兩個排序相鄰的聯通塊x,y。他們的權值和分別是Sx,Sy,大小分別是Zx,Zy
那麼x應當在y前面,當且僅當ZxSy>ZySx
移項
SxZ

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); }