题解 bzoj 2151 种树

题意

传送门

手写堆大法好啊,题解貌似没有结构体堆的做法,思路有些像配对堆,关于配对堆请自行百度,因为本蒟蒻不会。。

以下是蒟蒻的做法:建立一个大根堆a维护最大价值里面存入它的编号以及价值。听说配对堆可以不用定义结构体,但我不会呀!l表示这个位置左边的编号,r同理。一个数组book标记是否可以选择,每次选完之后左右两边进行标记,堆中加入左右之和减去本身的值再将其放入原来的位置,即可达到后悔操作,更新左右编号,然后取m次即可。

#include<bits/stdc++.h>
using namespace std;
int n,m,add,k;
struct node{
	int data;//价值
	int num;//编号
}a[200010];
int c[200010];
int l[200010],r[200010];
bool book[200010];
long long ans;
void shiftdown(int x){//下调操作,依靠价值进行堆中元素调整
	int t,flag=0;
	while(2*x<=add&&flag==0){
		if(a[x].data<a[2*x].data) t=2*x;
		else t=x;
		if(2*x+1<=add&&a[t].data<a[2*x+1].data) t=2*x+1;
		if(t!=x){
			swap(a[t],a[x]);//这里是交换结构体哦
			x=t;
		}else flag=1;
	}
}
void shiftup(int x){//上调操作
	int flag=0;
	if(x==1) return;
	while(x!=1&&flag==0){
		if(a[x].data>a[x/2].data) swap(a[x],a[x/2]);
		else flag=1;
		x/=2;
	}
}
int main(){
	scanf("%d %d",&n,&m);
	if(m>(n>>1)){
		printf("Error!");
		return 0; 
	} 
	for(int i=1;i<=n;++i){
		scanf("%d",&c[i]);
		a[++add].data=c[i];
		a[add].num=i;
		l[i]=i-1;
		r[i]=i+1;
		shiftup(add);//每加入一个新的元素都要进行上调操作
	}
	l[1]=n,r[n]=1;//环形的
	ans=0;
	while(m--){//标记过的直接弹出
		int x=a[1].num,val=a[1].data;
		a[1]=a[add--];
		shiftdown(1);
		while(book[x]){//注意先把值取出来再弹出哦
			x=a[1].num,val=a[1].data;
			a[1]=a[add--];
			shiftdown(1);
		}
		ans+=val;
		c[x]=c[l[x]]+c[r[x]]-c[x];//左右减去本身
		book[l[x]]=1;//标记
		book[r[x]]=1;
		l[x]=l[l[x]];//新的左边的原来左边的左边
		r[x]=r[r[x]];//大体同上
		r[l[x]]=x;//左边的右边更新
		l[r[x]]=x;//右边的左边更新
		a[++add].data=c[x];
		a[add].num=x;//放入元素
		shiftup(add);
	}
	printf("%lld",ans);
	return 0;
} 
原文地址:https://www.cnblogs.com/donkey2603089141/p/11414959.html