【bzoj2151】种树(堆/优先队列+双向链表)
题目传送门: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; }