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 ",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(" "); rep(i,1,(1<<h)-1)ans[i]=0; ve[1].clear(); sum=0; } return 0; }