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;
}
View Code

 

posted on 2020-04-07 21:52  MZRONG  阅读(152)  评论(0编辑  收藏  举报