種樹 (堆模擬網絡流)
阿新 • • 發佈:2018-09-01
ace iostream () 輸入輸出 cond algorithm break etc air
因為先選了 i ,那麽下一次選擇的時候,如果反悔就是選兩邊
就是選了周圍兩個點,不反悔就是選其他點
那麽把周圍兩個點維護為一個點就可以了。
即 \(vi[i]=vi[l[i]]+vi[r[i]]-vi[i]\)
然後維護一下 \(l\) 和 \(r\) 數組。
種樹
題目描述
cyrcyr今天在種樹,他在一條直線上挖了n個坑。這n個坑都可以種樹,但為了保證每一棵樹都有充足的養料,cyrcyr不會在相鄰的兩個坑中種樹。而且由於cyrcyr的樹種不夠,他至多會種k棵樹。假設cyrcyr有某種神能力,能預知自己在某個坑種樹的獲利會是多少(可能為負),請你幫助他計算出他的最大獲利。
輸入輸出格式
輸入格式:
第一行,兩個正整數n,k。
第二行,n個正整數,第i個數表示在直線上從左往右數第i個坑種樹的獲利。
輸出格式:
輸出1個數,表示cyrcyr種樹的最大獲利。
輸入輸出樣例
輸入樣例#1: 復制
6 3
100 1 -1 100 1 -1
輸出樣例#1: 復制
200
說明
對於20%的數據,n<=20。
對於50%的數據,n<=6000。
對於100%的數據,n<=500000,k<=n/2,在一個地方種樹獲利的絕對值在1000000以內。
題解
一道非常好的堆模擬網絡流題。
首先我們來想想\(O(n^2)\)
很顯然的兩重循環dp
第一重枚舉第i個位置,第二重枚舉選了j顆樹。
對於O(n)的模擬。
我們首先明確選了 i 就不能選 i-1 和 i+1。
所以所有的情況都是由選 i 和選 i-1 ,i+1 產生的。
選 i 時, i 的貢獻應當是大於 i-1 和 i+1 的。
但是當我們發現選 i-1 和 i+1 要更好時呢?
網絡流有一個操作就是反悔。
我們是不是也可以反悔一下呢?
就是選了周圍兩個點,不反悔就是選其他點
那麽把周圍兩個點維護為一個點就可以了。
即 \(vi[i]=vi[l[i]]+vi[r[i]]-vi[i]\)
然後維護一下 \(l\) 和 \(r\) 數組。
代碼
#include<cstdio> #include<cmath> #include<iostream> #include<algorithm> #include<cmath> #include<queue> using namespace std; priority_queue<pair<int,int> >q; int vis[1000001],ch[1000001],n,m; long long ans; struct node{ int vi,id,l,r; }t[1000001]; int read(){ int x=0,w=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); return x*w; } int main(){ n=read();m=read(); for(int i=1;i<=n;i++){ ch[i]=t[i].vi=read();t[i].id=i; t[i].l=i-1;t[i].r=i+1; q.push(make_pair(ch[i],i)); } t[0].r=1;t[n+1].l=n; while(m&&!q.empty()){ while(vis[q.top().second])q.pop(); int x=q.top().second;q.pop(); if(t[x].vi<0)break; ans+=t[x].vi; t[x].vi=t[t[x].l].vi+t[t[x].r].vi-t[x].vi; vis[t[x].l]=vis[t[x].r]=1; t[x].l=t[t[x].l].l;t[t[x].l].r=x; t[x].r=t[t[x].r].r;t[t[x].r].l=x; q.push(make_pair(t[x].vi,x)); m--; } printf("%lld",ans); return 0; }
種樹 (堆模擬網絡流)