codeforces#1329C - Drazil Likes Heap

C - Drazil Likes Heap

题意:

给定一个高度为$h$的完全二叉树,也满足最大堆的性质,执行下面操作,得到一个高度为$g$的完全二叉树

求操作后的完全二叉树的最小权值和,和操作的$id$

分析:

看了题解写的

从下往上考虑,定义h树为还没操作的树,定义g树为操作结束的树,经过观察发现,g树的叶子一定是当前位置在h树的最小节点,g树的其它节点只需要满足两个条件,第一,比它在g树的两个儿子要大,第二,来自于h树中当前位置的子树,所以贪心策略是一直找满足条件的最小值

构造方法:找到那些需要删除的节点,然后根据编号从大到小删除它们,因为大编号的删除不影响小编号的位置

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define per(i,a,b) for (int i=(b);i>=(a);i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef long long ll;
typedef vector<int> VI;
typedef pair<int,int> PII;

ll mod=998244353 ;
const int maxn=(1<<20)+7;
vector<PII>ve[maxn];
ll sum;
int ans[maxn],a[maxn],book[maxn],g,h;
void dfs(int x){
	if(x*2>(1<<h)-1){
		ve[x].pb(mp(a[x],x));
		return ;
	}
	dfs(x*2);
	dfs(x*2+1);
	int st=0,en=0;
	while(st<SZ(ve[x*2])&&en<SZ(ve[x*2+1])){
		if(ve[x*2][st]<ve[x*2+1][en])ve[x].pb(ve[x*2][st]),st++;
		else ve[x].pb(ve[x*2+1][en]),en++;
	}
	while(st<SZ(ve[x*2]))ve[x].pb(ve[x*2][st]),st++;
	while(en<SZ(ve[x*2+1]))ve[x].pb(ve[x*2+1][en]),en++;
	ve[x*2].clear();
	ve[x*2+1].clear();
	ve[x].pb(mp(a[x],x));
	if(x<=(1<<g)-1){
		int v=max(ans[x*2],ans[x*2+1]);
		//upper_bound(ve[x].begin(),ve[x].end(),mp(v,1e9));
		PII zz=*upper_bound(ve[x].begin(),ve[x].end(),mp(v,(int)1e9));
		ans[x]=zz.fi;
		book[zz.se]=1;
		sum+=ans[x];
	}
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d %d",&h,&g);
		rep(i,1,(1<<h)-1)scanf("%d",&a[i]);
		dfs(1);

		printf("%lld\n",sum);
		int fla=0;
		per(i,1,(1<<h)-1){
			if(book[i]==0){
				if(fla==0)printf("%d",i),fla=1;
				else printf(" %d",i);
			}else book[i]=0;
		}
		printf("\n");
		rep(i,1,(1<<h)-1)ans[i]=0;
		ve[1].clear();
		sum=0;
	}
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/carcar/p/12641971.html