CF R631 div2 1330 E Drazil Likes Heap
LINK:Drazil Likes Heap
那天打CF的时候 开场A读不懂题 B码了30min才过(当时我怀疑B我写的过于繁琐了。
C比B简单多了 随便yy了一个构造发现是对的。D也超级简单 dp了一下就没了。
但是到E就只剩下30min(都怪A B浪费我过多时间。
观察题目中给的一个程序 其维护了一个大根堆且这个程序意思是一个函数
这个函数是指对于这个大根堆上的一个非空节点来说每次会将这个值给删掉继承最大的儿子值 然后递归值最大的儿子值得某个节点没有一个非空儿子(那么这个点的值被删掉为0.
题目想让我们进行K次操作 每次操作都进行调用一次f函数 并且K次操作后保证是一个二叉树 并且维护二叉树的形态。
看起来很复杂 但其实发现在某个点x删除 比在x的儿子处删除要优 且x的删除对x的整棵总删除次数没有任何影响。
而且可以证明 光删父亲比两个儿子组合删最后删一次父亲不会更差。所以就不存在先删小的会比先删大的要优了。
所以这是一个裸的贪心 我们直接按照下标贪心的删除即可。考虑到什么时候不能删?每次我们删除某个节点的时候可以提前知道删除的会是哪个位置以此来判断即可。
删完一个节点后 位置的更新由于是二叉树所以为logn的高度 总复杂度nlogn.值得注意删除的过程其实我们在手写大根堆。
const int MAXN=2100000;
int T,n,m,maxx,top;ll cnt;
int a[MAXN],pos[MAXN],d[MAXN],ans[MAXN];
inline void gx(int x)
{
if(!a[x<<1]&&!a[x<<1|1])pos[x]=x;
else
{
if(a[x<<1]>a[x<<1|1])pos[x]=pos[x<<1];
else pos[x]=pos[x<<1|1];
}
}
inline void del(int x)
{
if(pos[x]==x){a[x]=0;return;}
if(a[x<<1]>a[x<<1|1])a[x]=a[x<<1],del(x<<1);
else a[x]=a[x<<1|1],del(x<<1|1);
gx(x);
}
int main()
{
freopen("1.in","r",stdin);
get(T);
while(T--)
{
get(n);get(m);
maxx=(1<<n)-1;cnt=top=0;
rep(1,maxx,i)get(a[i]),d[i]=d[i>>1]+1,cnt+=a[i];
rep(maxx+1,(1<<(n+1))-1,i)a[i]=0;
fep(maxx,1,i)gx(i);
int ww=(1<<m)-1;
rep(1,ww,i)
{
while(d[pos[i]]>m)
{
ans[++top]=i;
cnt-=a[i];
del(i);
}
}
putl(cnt);
rep(1,top,i)printf("%d ",ans[i]);
if(T)puts("");
}
return 0;
}