這個東西好神仙啊.jpgnode
$Orz laofu$ios
給你$n$個老鼠,$m$個洞,求一個知足要求的匹配的代價。優化
擁有一個限制:只能向左走。spa
直接排序便可。code
無限制。排序
顯然能夠給出DP方程:$f[i][j]$表示,前$i$個位置,有 $ j $ 個洞須要匹配,其中$j$能夠爲負,表示的意義爲有$-j$個老鼠須要匹配。get
顯然,這個一種常見的思路是將每個距離拆開看,爲$|x_i-y_j|$。string
因爲在匹配過程當中,交叉匹配必定會有不比它差的非交叉匹配方案,因此咱們將全部的交叉匹配方式去掉。it
可是,顯然這樣轉移是沒有辦法優化掉的,是滿的$n^2$,因此咱們考慮如何經過分析性質將其優化掉。io
如今咱們發現,對於上述DP方程,僅有直接覆蓋決策和區間擡升。
簡易代碼以下:
stack sz,sf; int tag1=0,tag2=0,f0=0; for(int i=1;i<=n;i++) { if(op[i]==1)//洞 { tag1-=a[i],tag2+=a[i]; sf.push(f0+a[i]-tag2); f0=min(sz.top()+tag1-a[i],f0);sz.pop(); }else // 鼠 { tag1+=a[i],tag2-=a[i]; sz.push(f0+a[i]-tag1); f0=sf.top()+tag2-a[i];sf.pop(); } }
顯然,對於這種匹配問題,必定不會存在匹配交叉的狀況,因此咱們考慮在此的基礎上進行貪心。
對於上述DP方程,咱們給出以下差分結果:
$\begin{cases} d[i][j]=f[i][j]-f[i][j-1] , j>0 \ d[i][j]=f[i][j]-f[i][j+1],j<0\end{cases}$
那麼給出以下轉移,因爲咱們發現,經過上述表達,只能表達出$f[i][j]$之間的關係,沒法準確的表達出$f[i][j]$,因此咱們須要維護一個$f[i][0]$來獲得因此的關係。
那麼給出轉移:
對於老鼠:$f[i][0]=f[i-1][0]+d[i-1][1]+a[i],d[i][j]=\begin{cases}d[i-1][j+1] , j>0||j<-1\-d[i-1][1]-x[i]\times 2\end{cases}$
對於洞:$f[i][0]=\min(f[i-1][0],f[i-1][0]+d[i-1][-1]+a[i])$
拆開看:
這個東西顯然就能夠用上面的那個棧的作法用差分意義理解了...
有這個東西,能夠發現這個DP是具備凸性的,也就是說,對於這個DP來講,$d[i][j]$在$j>0$時單調遞增,在$j<0$時單調遞減。
因此直接用堆來維護一下最小的$d[i][j],j>0$和最小的$d[i][j],j<0$便可。
實現代碼以下:
priority_queue<int>q0,q1; for(int i=1;i<=n;i++) if(op[i]==1)//洞 { if(q0.top()+a[i]<0) { f0=f0+q0.top()+a[i]; q1.push(-q0.top()-a[i]*2); q0.pop(); }else q1.push(-a[i]); }else // 鼠 { f0=f0+q1.top()+a[i]; q0.push(-q1.top()-a[i]*2); q1.pop(); }
另外的一種分析方式:
對於當前的兩個堆,分別至關因而對於鼠和對於洞的匹配集合,每次強制老鼠匹配一個還沒有匹配的,在他左邊的洞,而後能夠在以後的操做中將此次匹配反悔,變成匹配以後的某一個洞。
如今,對於給定問題,老鼠只能向左走,而且代價爲$ x_i-y_j+w_j $,不必定每一個老鼠都進入洞中,求最大代價。
好像這個題能夠隨便作的樣子...
直接按照$a_i$從左到右的順序,維護一個對於洞的堆,其中的比較關鍵字是按照$w_j-y_j$從大到小排序。
而後每次取出堆頂進行匹配便可...
對於每一個洞,有一個容量限制,也就是每一個洞能夠容納$b_i$個老鼠。
好的,咱們接下來考慮這個題如何處理...
只須要強制往左跑,和強制往右跑的構成的堆的交集作一發便可。
這個東西的時間複雜度顯然是$O(n\log n)$的。
對此的證實:顯然我不會。
顯然,只須要在維護堆的時候,傳進兩個參數,分別表示剩餘的容量,和相應的代價,而後按照第二維維護最小值便可。
每次把對應容量減去,若是爲$0$就再也不壓入...
大體代碼:
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <cstdlib> #include <queue> #include <iostream> #include <bitset> using namespace std; #define N 200005 #define ll long long const long long inf = 1ll<<40; struct node{int op,lim;ll x,w;}a[N]; bool cmp(const node &a,const node &b){return a.x<b.x;} int tot,n,m;ll f0,sum; priority_queue<pair<ll ,int > ,vector<pair<ll ,int > > ,greater<pair<ll ,int > > >q1; priority_queue<ll ,vector<ll > ,greater<ll > >q0; int main() { scanf("%d%d",&n,&m); for(int i=1,x;i<=n;i++)scanf("%d",&x),a[i]=(node){0,0,x,0};tot=n; for(int i=1;i<=m;i++)tot++,scanf("%lld%lld%d",&a[tot].x,&a[tot].w,&a[tot].lim),a[tot].op=1,sum+=a[tot].lim; a[++tot]=(node){1,n,-inf,0};a[++tot]=(node){1,n,inf,0}; if(sum<n)return puts("-1"),0;sort(a+1,a+tot+1,cmp); for(int i=1;i<=tot;i++) if(a[i].op==1)//洞 { int t=a[i].lim; while(!q0.empty()&&t&&q0.top()+a[i].x<0) { f0=f0+q0.top()+a[i].x; q1.push(make_pair(-q0.top()-a[i].x*2,1)); q0.pop();t--; } if(t)q1.push(make_pair(-a[i].x,t)); }else // 鼠 { pair<ll ,int > tmp=q1.top();q1.pop(); f0=f0+tmp.first+a[i].x; q0.push(-tmp.first-a[i].x*2);tmp.second--; if(tmp.second)q1.push(tmp); } printf("%lld\n",f0); }
每一個老鼠能夠無限分身,可是分出來的身必定要進入洞中,每一個洞有一個容量無限,可是至少有一隻老鼠。
而後彷佛正解也不是很難,咱們只須要這樣考慮,對於每一個老鼠的權值,分紅兩類,一部分,容量爲$1$,費用爲$-\infty+d[i][1]+x_i\times 2$,一部分容量爲$\infty$,費用爲$d[i][1]+x[i]\times 2$
而後對於每一個洞,也就一樣,當作Pro4中容量上限爲$\infty$來作。
而後咱們考慮到,兩個容量$\infty$之間匹配,必定不優,因此不存在這種匹配狀況。
大體代碼:
priority_queue<pair<int ,int > >q0,q1; for(int i=1;i<=n;i++) if(op[i]==1)//洞 { pair<int ,int >tmp;tmp=q0.top();q0.pop(); if(tmp.first+a[i]-inf<0) { f0=f0+tmp.first+a[i]; q1.push(make_pair(tmp.first,1));tmp.second--; if(!tmp.second)tmp=q0.top(); }else q1.push(make_pair(a[i]-inf,1)); while(tmp.first+a[i]<0) { f0=f0+tmp.first+a[i];q0.pop(); q1.push(make_pair(-tmp.first-a[i]*2,1));tmp.second--; if(tmp.second)q0.push(tmp); } q1.push(a[i],inf); }else // 鼠 { pair<int ,int >tmp;tmp=q1.top();q1.pop(); if(tmp.first+a[i]-inf<0) { f0=f0+tmp.first+a[i]; q0.push(make_pair(tmp.first,1));tmp.second--; if(!tmp.second)tmp=q1.top(); }else q0.push(make_pair(a[i]-inf,1)); while(tmp.first+a[i]<0) { f0=f0+tmp.first+a[i];q1.pop(); q0.push(make_pair(-tmp.first-a[i]*2,1));tmp.second--; if(tmp.second)q1.push(tmp); } q0.push(a[i],inf); }
在最基礎的模型上增長,每一個洞有一個權值$w_i$,老鼠進洞的代價變爲:$|x_i-y_i|+w_i$
想明白了,也不是很難。
只須要考慮到上述式子在不一樣狀況下,代價不一樣,分別是$w_i-y_i$和$w_i+y_i$,而後分別做爲洞和老鼠的反悔狀況。
這樣的意義表明着,老鼠即便匹配了右側的一個洞,也可能會反悔去匹配更右側的一個洞,由於這樣可能代價更小。
其餘的東西卻是沒啥區別。
簡易代碼以下:
priority_queue<int>q0,q1; for(int i=1;i<=n;i++) if(op[i]==1)//洞 { if(q0.top()+a[i]+w[i]<0) { f0=f0+q0.top()+a[i]+w[i]; q1.push(-q0.top()-a[i]*2); q0.pop(); q0.push(-a[i]-w[i]); }else q1.push(-a[i]+w[i]); }else // 鼠 { f0=f0+q1.top()+a[i]; q0.push(-q1.top()-a[i]*2); q1.pop(); }
咱們能夠考慮,若是模型轉化到樹上了,如何解決。
(可能大概這個題能夠被一眼秒掉了。
直接把堆換成可並堆,一樣,它也不能交叉匹配,這裏的交叉匹配是指在同一條鏈上時,二者交叉匹配。
沒有簡易代碼
其實仍是最基礎的模型,上面加上了容量和額外代價。
直接把上面Pro 6和Pro 4的作法結合起來便可。
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <cstdlib> #include <queue> #include <iostream> #include <bitset> using namespace std; #define N 200005 #define ll long long const long long inf = 1ll<<40; struct node{int op,lim;ll x,w;}a[N]; bool cmp(const node &a,const node &b){return a.x<b.x;} int tot,n,m;ll f0,sum; priority_queue<pair<ll ,int > ,vector<pair<ll ,int > > ,greater<pair<ll ,int > > >q1,q0; int main() { scanf("%d%d",&n,&m); for(int i=1,x;i<=n;i++)scanf("%d",&x),a[i]=(node){0,0,x,0};tot=n; for(int i=1;i<=m;i++)tot++,scanf("%lld%lld%d",&a[tot].x,&a[tot].w,&a[tot].lim),a[tot].op=1,sum+=a[tot].lim; a[++tot]=(node){1,n,-inf,0};a[++tot]=(node){1,n,inf,0}; if(sum<n)return puts("-1"),0;sort(a+1,a+tot+1,cmp); pair<ll ,int > tmp; for(int i=1;i<=tot;i++) if(a[i].op==1)//洞 { int t=a[i].lim; while(!q0.empty()&&t&&q0.top().first+a[i].w+a[i].x<0) { tmp=q0.top();q0.pop(); int now=min(t,tmp.second); f0=f0+(ll)now*(tmp.first+a[i].x+a[i].w); q1.push(make_pair(-tmp.first-a[i].x*2,now)); t-=now;tmp.second-=now;if(tmp.second)q0.push(tmp); } if(a[i].lim!=t)q0.push(make_pair(-a[i].x-a[i].w,a[i].lim-t)); if(t)q1.push(make_pair(-a[i].x+a[i].w,t)); }else // 鼠 { tmp=q1.top();q1.pop(); f0=f0+tmp.first+a[i].x; q0.push(make_pair(-tmp.first-a[i].x*2,1));tmp.second--; if(tmp.second)q1.push(tmp); } printf("%lld\n",f0); }
沒看懂這題在說些啥
給一個序列,要求選出$K$個區間,每一個區間不相交,求最大和。
直接線段樹維護區間連續最大值,每次區間取相反數便可。
看樣子像是Pro 9 + Pro 10的合體,反正不太可寫...
預估代碼會和蜀道難有一拼了.jpg
暴力的話,費用流直接建圖應該有$40\sim60$分不等,我不知道我本身哪裏寫掛掉了,wa了好幾個點...
因爲咱們發現,隨着天數的增長,選擇的蔬菜的集合只會變成原先的父集,也就是不會退流,因此直接使用線段樹維護增廣的合法性便可,剩下的就是堆+貪心了。
有一棵$n $個點的樹,$m$我的站在樹根。每條邊有一個邊權。如今每一個人均可以任意行走,通過一條邊就要付出對應邊權的代價。問最小代價使得每條邊至少被一我的通過。$n \le 10^5,m \le 50$。
直接DP不解釋。