https://codeforces.com/contest/1330/problem/E
有一个高度为h的大顶堆:有2h -1个不同的正整数,下标从1到2h−1,1<i<2h, a[i]<a[⌊i/2⌋].
现在我们要降低堆的高度,为h,有2g-1个整数,那么我们要删掉2h-2g个数;
选择索引 i 删除,删除方法如下:
被删除的节点值为0,代表该节点不存在。
所以操作后,2g-1个元素的下标在[1,2g-1]范围内;使得剩余元素的总和最小,并且输出调用删除函数时删除的节点下标。
人话:将高度为h的完全大顶堆删除一部分节点变成高度位g的完全大顶堆,使得剩余部分和最小。
哈,此题不会,题意也很模糊,官方题解太长了,还是英文,要死了,直接看的博客,发现一个简单明了的;
参考博客:https://blog.csdn.net/qq_45458915/article/details/105309861?%3E
思路:删完后仍是完全大顶堆,那么首先保证叶子节点的高度为g,然后尽量删除值最大的节点,删到不能删为止,再删除下一个值较大的节点,也就是贪心的从根节点开始删除,只要满足要删除的节点删除后叶子节点的高度大于等于g就可以了,总之能删就删,不能删就删下一个节点。
#include <bits/stdc++.h> using namespace std; const int MAXN=1e5+5; const int mod=1e9+7; typedef long long ll; //typedef __int128 LL; const int inf=0x3f3f3f3f; const long long INF=0x3f3f3f3f3f3f3f3f; int a[(1<<(20+1))+10]; vector<int>ans; int getid(int k) { if(a[k<<1]==0&&a[(k<<1)+1]==0)return k; else if(a[k<<1]>a[(k<<1)+1])return getid(k<<1); else return getid((k<<1)+1); } void dfs(int k) { if(a[k<<1]==0&&a[(k<<1)+1]==0) { a[k]=0; return ; } else if(a[k<<1]>a[(k<<1)+1])a[k]=a[k<<1],dfs(k<<1); else a[k]=a[(k<<1)+1],dfs((k<<1)+1); } int main() { int t; scanf("%d",&t); while(t--) { int h,g; scanf("%d%d",&h,&g); for(int i=1;i<=1<<(h+1);i++)a[i]=0; ans.clear(); for(int i=1;i<=(1<<h)-1;i++)scanf("%d",&a[i]); int limit=(1<<g)-1; for(int i=1;i<=(1<<g)-1;i++) { while(getid(i)>limit) { ans.push_back(i); dfs(i); } } ll sum=0; for(int i=1;i<=(1<<g)-1;i++)sum+=a[i]; printf("%lld\n",sum); for(int i=0;i<ans.size();i++)printf("%d%c",ans[i],i==ans.size()-1?'\n':' '); } return 0; }