zoukankan      html  css  js  c++  java
  • [BZOJ2809]dispatching(左偏树)

    题面

    http://darkbzoj.tk/problem/2809

    题解

    前置知识

    可以先按上级-下级的关系建出一棵关系树,然后将在这个树上按dfs的顺序枚举题目中的“管理者”u。容易发现派遣的所有忍者都在u的子树中。而根据贪心思想,显然应该按照薪水从小到大选取u子树中的节点,直到即将超出预算。

    对于每一个管理者,维护一个大根堆,里面存的是依照上述过程选出来的所有点的薪水值。那么点u的堆就是u的所有子节点的堆,以及C[u],这些东西全部合并;然后,每次删除最大值,直到堆中总和不超过预算。点u作为管理者对应的最佳答案就是此时堆的大小*L[u]。最后对所有u的答案求最大值即可。

    合并堆的过程可以使用左偏树来实现。每个点最多入堆1次,出堆1次。合并总次数O(n)。故总时间复杂度(O(n log n))

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long 
    #define In inline 
    #define rg register
    
    const ll N = 1e5;
    
    In ll read(){
    	ll s = 0,ww = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
    	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
    	return s * ww;
    }
    
    ll C[N+5],L[N+5];
    
    struct LftTree{
    	ll cnt,c[N+5][2],val[N+5],dis[N+5],sum[N+5],num[N+5];
    	void reset(){
    		dis[0] = -1;
    		cnt = 0;
    	}
    	int create(int i){
    		cnt++;
    		num[cnt] = 1;
    		sum[cnt] = val[cnt] = C[i];
    		return cnt;
    	}		
    	int merge(int u,int v){
    		if(!u || !v)return u + v;
    		if(val[u] < val[v])swap(u,v);
    		c[u][1] = merge(c[u][1],v);
    		if(dis[c[u][0]] < dis[c[u][1]])swap(c[u][0],c[u][1]);
    		dis[u] = dis[c[u][1]] + 1;
    		sum[u] = sum[c[u][0]] + sum[c[u][1]] + val[u];
    		num[u] = num[c[u][0]] + num[c[u][1]] + 1;
    		return u;
    	}
    	int pop(int u){
    		return merge(c[u][0],c[u][1]);
    	}
    }T;
    
    ll n,m,rt,ans;
    ll head[N+5],cnt,root[N+5];
    
    struct node{
    	int des,next;
    }e[N+5];
    
    In void addedge(int a,int b){
    	cnt++;
    	e[cnt].des = b;
    	e[cnt].next = head[a];
    	head[a] = cnt;
    }
    
    void dfs(int u){
    	root[u] = T.create(u);
    	for(rg int i = head[u];i;i = e[i].next){
    		int v = e[i].des;
    		dfs(v);
    		root[u] = T.merge(root[u],root[v]);
    	}
    	while(T.sum[root[u]] > m)root[u] = T.pop(root[u]);
    	ans = max(ans,L[u] * T.num[root[u]]);
    }
    
    int main(){
    	n = read(),m = read();
    	T.reset();
    	for(rg int i = 1;i <= n;i++){
    		ll fa = read();
    		if(!fa)rt = i;
    		else addedge(fa,i);
    		C[i] = read(),L[i] = read();
    	}	
    	dfs(rt);
    	cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    实验四 代码评审
    实验三、UML 建模工具的安装与使用
    结对编程 第二阶段
    结对编程第一阶段
    实验一 GIT代码版本管理
    实验五 单元测试
    实验四 代码评审
    实验三 UML 建模工具的安装与使用
    结对编程阶段二
    结对编程第一阶段
  • 原文地址:https://www.cnblogs.com/xh092113/p/12346383.html
Copyright © 2011-2022 走看看