题解 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值