1. 程式人生 > >【bzoj2151】種樹(堆/優先隊列+雙向鏈表)

【bzoj2151】種樹(堆/優先隊列+雙向鏈表)

getc lin pos return tar ror del 刪掉 位置

  題目傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=2151

  這道題因為優先隊列不怎麽會用,而且手寫堆的代碼也不長,也想復習一下手寫堆的寫法……結果……WAWAWAWAW……看來我對堆的認識還是太淺薄了……

  這道題,如果沒有限制不能選相鄰的兩個位置,那就肯定是貪心地選擇m個美觀度最大的位置種樹。然而這裏加了限制,那麽我們可以註意到,如果一個美觀度比較大的位置不被選上,一定是因為它兩邊的位置都被選了。

  於是我們可以把n個美觀度壓進一個堆裏,每次把美觀度最大的位置取出來更新答案,然後它兩邊的位置刪掉,把這個位置的美觀度修改成(左邊的美觀度+右邊的美觀度-原美觀度),就是選這個位置與選兩邊位置的權值差。

  如果細節沒有打錯,然後就可以愉快地AC這道題了。

  跑的慢也不會贏的代碼:

技術分享圖片
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define inf 1<<30;
#define
min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) ll read() { ll tmp=0; char f=1,c=getchar(); while(c<0||9<c){if(c==-)f=-1; c=getchar();} while(0<=c&&c<=9){tmp=tmp*10+c-0; c=getchar();} return tmp*f; } using namespace std; struct data{ int
w,id; }hp[200010]; int l[200010],r[200010],pos[200010]; int n,m,tot=0; void swap(int x,int y) { data tmp=hp[x]; hp[x]=hp[y]; hp[y]=tmp; pos[hp[x].id]=x; pos[hp[y].id]=y; } void add(int id,int w) { hp[++tot].id=id; hp[tot].w=w; pos[id]=tot; for(int now=tot;now>1&&hp[now].w>hp[now>>1].w;now>>=1)swap(now,now>>1); } void sift(int x) { int now=x; while(now<<1<=tot){ int ne=now<<1; if(ne<tot&&hp[ne].w<hp[ne+1].w)++ne; if(hp[ne].w>hp[now].w)swap(ne,now),now=ne; else break; } } void del(int id) { hp[pos[id]].w=-inf; sift(pos[id]); r[l[id]]=r[id]; l[r[id]]=l[id]; } int main() { int i; n=read(); m=read(); if(n<m*2){ printf("Error!\n"); return 0; } for(i=1;i<=n;i++)add(i,read()),l[i]=i-1,r[i]=i+1; l[1]=n; r[n]=1; ll ans=0; while(m--){ ans+=hp[1].w; int id=hp[1].id; hp[1].w=hp[pos[l[id]]].w+hp[pos[r[id]]].w-hp[1].w; sift(1); del(l[id]); del(r[id]); } printf("%lld\n",ans); return 0; }
bzoj2151

【bzoj2151】種樹(堆/優先隊列+雙向鏈表)