bzoj2809[Apio2012]dispatching
题意:
n个点组成一棵树,每个点都有一个领导力和费用,可以让一个点当领导,然后在这个点的子树中选择一些费用之和不超过m的点,得到领导的领导力乘选择的点的个数(领导可不被选择)的利润。求利润最大值。n≤100000
题解:
可并堆。可以得到一个结论,就是在子树中选点的时候,先选所有点,如果费用超了,就不断把费用最大的剔除,直到费用不超,这样得到的选点数量最大,这过程可以用堆维护。同时每个点的堆都可以由子树的堆合并得到,所以需要可并堆。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 100100 6 #define ll long long 7 using namespace std; 8 9 int ch[maxn][2],rt[maxn],n,m,st; ll sz[maxn],v[maxn],ans,sm[maxn],gd[maxn]; 10 struct e{int t,n;}; e es[maxn]; int ess,g[maxn]; 11 void pe(int f,int t){es[++ess]=(e){t,g[f]}; g[f]=ess;} 12 void init(){ess=0; memset(g,0,sizeof(g));} 13 void update(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1; sm[x]=sm[ch[x][0]]+sm[ch[x][1]]+v[x];} 14 int merge(int x,int y){ 15 if(!x||!y)return x+y; if(v[x]<v[y])swap(x,y); ch[x][1]=merge(ch[x][1],y); 16 swap(ch[x][0],ch[x][1]); update(x); return x; 17 } 18 void pop(int &x){ 19 int y=merge(ch[x][0],ch[x][1]); ch[x][0]=ch[x][1]=0; sz[x]=1; sm[x]=v[x]; x=y; 20 } 21 void dfs(int x){ 22 rt[x]=x; for(int i=g[x];i;i=es[i].n)dfs(es[i].t),rt[x]=merge(rt[x],rt[es[i].t]); 23 while(sz[rt[x]]&&sm[rt[x]]>m)pop(rt[x]); ans=max(ans,sz[rt[x]]*gd[x]); 24 } 25 int main(){ 26 scanf("%d%d",&n,&m); init(); 27 inc(i,1,n){ 28 int a; ll b,c; scanf("%d%lld%lld",&a,&b,&c); if(a)pe(a,i);else st=i; 29 v[i]=sm[i]=b; sz[i]=1; gd[i]=c; ch[i][0]=ch[i][1]=rt[i]=0; 30 } 31 ans=0; dfs(st); printf("%lld",ans); return 0; 32 }
20160531