zoukankan      html  css  js  c++  java
  • luogu P1552 [APIO2012]派遣 题解--可并堆/贪心

    • 题目链接:

      https://www.luogu.org/problemnew/show/P1552

    • 分析:

      一开始愣是没看懂题,后面发现就是你要找一个树上点集使得各点权值之和小于(M),并且找一个点集的公共祖先(Anc)(管理者),使(Anc)的领导力乘以点集大小最大

      一开始想DP,一看数据范围,我们可以稍微暴力一点,枚举每个点作为管理者的答案最大值,我们只要在子树中权值最小的点选起使权值之和小于(M)就可以了,一下问题简单了许多.

      再暗中观察分析,发现这个信息是可以维护转移的,在两颗子树合并时可以用大根堆搞一搞.我们不妨先对树进行(dfs),中途不断合并权值信息.

      (dfs) (x)节点时,以(x)为根建一个大根堆称为x的代表堆,(dfs)(x)的一个子树回溯到(x)时,将子树的代表堆与(x)的代表堆进行合并,同时将两堆权值之和相加.若权值之和大于(m),由于是大根堆,不断(pop)堆顶直至小于(m),满足最优子结构.

    • 注意

      本题思维容易但要注意一些问题:

      1. 虽说是X的代表堆,然而X可能会被(pop)这个堆,于是要用一个(root)记录代表堆的根,同时dfs返回该root值

      2. 可并堆可用斜堆或左偏堆 COGS上斜堆略胜一筹,luogu上却不尽人意

      3. 还有一些细节和小技巧,比如处理祖宗的最大领导力可以看代码

    • 代码(左偏树)

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <queue>
    #define ri register int 
    #define ll long long 
    using namespace std;
    const int maxn=103005;
    const int inf=0x7fffffff;
    template <class T>inline void read(T &x){
    	x=0;int ne=0;char c;
    	while(!isdigit(c=getchar()))ne=c=='-';
    	x=c-48;
    	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    	x=ne?-x:x;
    	return ;
    }
    int n;
    ll  m,leader[maxn],key[maxn],ans[maxn],anss=-inf;
    int ls[maxn],rs[maxn],fa[maxn],cnt[maxn],npl[maxn];//可并堆
    vector <int>son[maxn];
    int get(int x){
    	while(x!=fa[x])fa[x]=get(fa[x]);
    	return fa[x];
    }
    int merge(int x,int y){
    	if(!x)return y;
    	if(!y)return x;
    	if(key[x]<key[y])swap(x,y);
    	fa[y]=x;
    	rs[x]=merge(rs[x],y);
    	if(!ls[x]||npl[ls[x]]<npl[rs[x]])swap(ls[x],rs[x]);
    	if(!rs[x])npl[x]=0;
    	else npl[x]=npl[rs[x]]+1;
    	//swap(ls[x],rs[x]);
    	return x;
    }
    int del(int now){
    	fa[now]=-1;
    	fa[ls[now]]=fa[rs[now]]=0;
    	return merge(ls[now],rs[now]);
    }
    int dfs(int now,ll mx){//mx--祖先上的领导力最大值
    	int s,root=now,ro_s;
    	mx=max(mx,leader[now]);
    	ans[now]=key[now],cnt[now]=1;
    	anss=max(anss,leader[now]);
    	for(ri i=0;i<son[now].size();i++){		
    		s=son[now][i];		
    		ro_s=dfs(s,mx);	
    		ans[now]+=ans[s],cnt[now]+=cnt[s];
    		root=merge(root,ro_s);	
    		while(ans[now]>m&&cnt[now]) {
    			ans[now]-=key[root];
    			cnt[now]--;
    			root=del(root);
    		}
    		anss=max(anss,cnt[now]*mx);
    	}
    	return root;
    }
    int main(){
    	int pa;
    	//freopen("dispatching.in","r",stdin);
    	//freopen("dispatching.out","w",stdout);
    	read(n),read(m);
    	for(ri i=1;i<=n;i++){
    		read(pa),read(key[i]),read(leader[i]);
    		ls[i]=rs[i]=0,fa[i]=i,npl[i]=0;
    		son[pa].push_back(i);
    	}
    	dfs(1,leader[1]);
    	printf("%lld
    ",anss);
    	fclose(stdin);
    	fclose(stdout);
    	return  0;
    }
    
  • 相关阅读:
    Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2)
    Codeforces Round #592 (Div. 2)
    日常杂谈
    vc_redist x64 或者x86下载地址
    windows terminal编译实录
    刷机,twrp,安装xposed
    博客迁移公告
    tcpdump实用笔记
    分享一篇企鹅的暑期实习生技术面经验
    visudo使用笔记
  • 原文地址:https://www.cnblogs.com/Rye-Catcher/p/9260888.html
Copyright © 2011-2022 走看看