1. 程式人生 > 其它 >決策單調性&wqs二分

決策單調性&wqs二分

神奇的 dp 科技

其實是一個還算 trivial 的知識點吧……早在 2019 年我就接觸過了,然鵝當時由於沒認真學並沒有把自己學懂,故今復學之(

1. 決策單調性

引入:在求解 DP 問題的過程中我們常常遇到這樣的問題:我們列出了一個 \(dp\) 狀態轉移方程式形如 \(dp_i=\min\limits_{j<i}dp_j+w(j+1,i)\) 或類似的形式,暴力轉移時間複雜度 \(\mathcal O(n^2)\) 過不去,但是你發現這裡的代價函式 \(w(l,r)\) 有一些比較好的性質,譬如單調性或凹凸性等,此時你可以考慮使用決策單調性優化 \(dp\)

決策單調性,說白了就是假設 \(dp_i\)

\(dp_{from_i}\) 轉移來,那麼如果 \(from\) 陣列滿足 \(\forall j<i,from_j\le from_i\),就稱此處的 \(dp\) 滿足決策單調性(當然有可能 \(dp_i\) 從多個 \(dp_j\) 轉移過來都是最優的,此時決策單調性的定義就是存在一個 \(j\) 的最優決策點 \(\le\) 某個 \(i\) 的最優決策點)。

那麼問題就來了,如果僅僅通過打表找規律的方法列出決策點那顯然費時費力,有沒有不失去一般性和快捷性的判定方法呢?

注:下文中若無特殊說明,預設為最小化問題,若為最大化問題一般都要將不等號方向反過來。

判定決策單調性的強有力工具:四邊形不等式

四邊形不等式,說白了就是如果對於任意四個滿足 \(a\le b\le c\le d\) 的位置 \(a,b,c,d\),都有 \(w(a,c)+w(b,d)\le w(a,d)+w(b,c)\)(當然如果是最大化問題則要將不等號方向反過來,可以簡單記為“相交優於包含”),那麼對應的 \(dp\) 陣列則滿足決策單調性。

證明:考慮反證法,假設存在某個 \(y<x\) 滿足 \(from_x<from_y\)。方便起見下文中令 \(fx=from_x,fy=from_y\)

那麼根據 \(from\) 陣列的定義顯然有 \(dp_{fx}+w(fx+1,x)<dp_{fy}+w(fy+1,x)\ \ \ \ \ (1)\)

由於 \(fx+1<fy+1\le y<x\),根據四邊形不等式有 \(w(fx+1,y)+w(fy+1,x)\le w(fx+1,x)+w(fy+1,y)\ \ \ \ (2)\)

\((1)+(2)\),整理得 \(dp_{fx}+w(fx+1,y)<dp_{fy}+w(fy+1,y)\),顯然與最優決策點的定義相悖。

區間包含單調

對於任意 \(a\le b\le c\le d\),都有 \(w(b,c)\le w(a,d)\),則稱 \(w\) 包含單調。

包含單調似乎不能直接用來證決策單調性,因此要配合一些東西食用。

一些推論

沒錯這東西還有一些推論。

推論 \(1\):如果 \(w\) 函式滿足 \(w(l+1,r)+w(l,r-1)\le w(l,r)+w(l+1,r-1)\),那麼 \(w\) 滿足四邊形不等式。

證明:將式子變形得到 \(w(l,r)-w(l,r-1)\ge w(l+1,r)-w(l+1,r-1)\)

推廣可得 \(w(l,r)-w(l,r-1)\ge w(l+k,r)-w(l+k,r-1),k\in\mathbb{Z}^+,l+k\le r-1\)

移項可得 \(w(l,r)-w(l+k,r)\ge w(l,r-1)-w(l+k,r-1)\)

進一步推廣可得 \(w(l,r)-w(l+k,r)\ge w(l,r-p)-w(l+k,r-p),p\in\mathbb{Z}^+,l+k\le r-p\)

移項可得 \(w(l,r-p)+w(l+k,r)\le w(l,r)+w(l+k,r-p)\)

\(a=l,b=l+k,c=r-p,d=r\),那麼上式即可寫作 \(w(a,c)+w(b,d)\le w(a,d)+w(b,c)\)

推論 \(2\):如果某個 \(dp\) 方程滿足 \(dp_{i,j}=\min\limits_{k}(dp_{i,k}+dp_{k+1,j}+w(i,j))\),且 \(w(i,j)\) 滿足包含單調和四邊形不等式,則 \(dp\) 陣列也滿足四邊形不等式。

證明:對區間長度歸納,顯然 \(a=b=c=d\) 時必然有 \(dp_{a,c}+dp_{b,d}\le dp_{a,d}+dp_{b,c}\)

我們要證明對於某個 \(a\le b\le c\le d\),其中 \(a\ne d\),有 \(dp_{a,c}+dp_{b,d}\le dp_{a,d}+dp_{b,c}\),考慮分情況討論:

  1. \(b=c\),那麼設 \(x\)\(dp_{a,d}\) 最優決策點,不妨設 \(x\le b\),對於另外一半映象一下即可,那麼

    \[\begin{aligned} &dp_{a,d}+dp_{b,c}\\ =&dp_{a,x}+dp_{x+1,d}+w(a,d)\\ \ge&dp_{a,x}+dp_{x+1,b}+dp_{b,d}+w(a,d)\\ \ge&dp_{a,x}+dp_{x+1,b}+dp_{b,d}+w(a,b)\\ \ge&dp_{a,b}+dp_{b,d} \end{aligned} \]
  2. \(b\ne c\),設 \(x\)\(dp_{a,d}\) 的最優決策點,\(y\)\(dp_{b,c}\) 的最優決策點,不妨設 \(x\le y\),那麼

    \[\begin{aligned} &dp_{a,d}+dp_{b,c}\\ =&dp_{a,x}+dp_{x+1,d}+w(a,d)+dp_{b,y}+dp_{y+1,c}+w(b,c)\\ =&dp_{a,x}+dp_{b,y}+(dp_{x+1,d}+dp_{y+1,c})+(w(a,d)+w(b,c))\\ \ge&dp_{a,x}+dp_{b,y}+(dp_{x+1,c}+dp_{y+1,d})+(w(a,d)+w(b,c))&(\text{對}x+1,y+1,c,d\text{進行四邊形不等式})\\ \ge&dp_{a,x}+dp_{b,y}+(dp_{x+1,c}+dp_{y+1,d})+(w(a,c)+w(b,d))\\ \ge&=dp_{a,c}+dp_{b,d} \end{aligned} \]

得證。

具體應用是可以優化區間 \(dp\),有興趣的可以自己去實現,反正我是沒興趣咯(大霧

決策單調性的實現

講了一車理論(fei)知識(hua),接下來談談決策單調性怎麼實現。

I. 單峰+1D:單指標跳躍

如果一個 \(dp\) 轉移方程滿足對於所有 \(dp_i\) 都有在 \(from_i\) 左邊的位置對 \(dp_i\) 的貢獻隨下標的增大而單調遞減,在 \(from_i\) 右邊的位置對 \(dp_i\) 的貢獻隨著下標的增大而增大,那麼結合決策單調性可以用一個指標維護決策點,每次求解某個 \(dp_i\) 就指標不斷向後跳直到下一個位置的貢獻劣於當前位置為止。

時間複雜度線性。

例題?抱歉,由於這種情況應用不是太廣泛,因此沒有例題……

II. 每一層之間的 \(dp\) 沒有影響:分治

這種情況的適用範圍為:二維 \(dp\),只有上一層向下一層轉移,同一層之間不會轉移。而且每一層中有決策單調性。

具體來說我們定義一個函式 solve(l,r,pl,pr) 表示當前處理區間 \([l,r]\),該區間中所有位置的決策點都在 \([pl,pr]\) 中,記 \(mid=\lfloor\dfrac{l+r}{2}\rfloor\),每次我們暴力列舉 \([pl,pr]\) 中的決策點更新 \(dp_{mid}\) 即可,我們假設決策點為 \(pos\),那麼我們繼續執行 solve(l,mid-1,pl,pos),solve(mid+1,r,pos,pr) 即可,正確性顯然,由於遞迴層數最多 \(\log n\),每層跳躍長度總和 \(\mathcal O(n)\),因此總複雜度 \(\mathcal O(n\log n)\)

有時候我們還會遇到這樣的問題:代價函式 \(w(l,r)\) 不好直接求,但利用類似於莫隊這種移指標的方式可以求得,譬如區間數顏色,此時也可以通過這種方式計算代價函式,由於每次移動左右端點都在候選區間中移,因此指標的移動次數也是 \(\mathcal O(n\log n)\) 的。

例題:

1. CF868F Yet Another Minimization Problem

模板題,記 \(w(l,r)\) 表示 \([l,r]\) 中相同數字的對數,那麼顯然有 \(w(l,r-1)+w(l+1,r)\le w(l,r)+w(l+1,r-1)\),且等號不成立當且僅當 \(a_l=a_r\),因此該 \(dp\) 是滿足決策單調性的,又因為同一層之間的 \(dp\) 互不影響,因此可以使用分治優化,複雜度 \(n\log n\)

const int MAXN=1e5;
const int MAXK=20;
int n,k,a[MAXN+5];ll dp[MAXN+5][MAXK+3];
int cl=1,cr=0,buc[MAXN+5];ll sum=0;
void push(int x){sum-=1ll*buc[a[x]]*(buc[a[x]]-1)>>1;buc[a[x]]++;sum+=1ll*buc[a[x]]*(buc[a[x]]-1)>>1;}
void pop(int x){sum-=1ll*buc[a[x]]*(buc[a[x]]-1)>>1;buc[a[x]]--;sum+=1ll*buc[a[x]]*(buc[a[x]]-1)>>1;}
ll calc(int l,int r){
	while(cr<r) push(++cr);
	while(cl>l) push(--cl);
	while(cl<l) pop(cl++);
	while(cr>r) pop(cr--);
	return sum;
}
void solve(int l,int r,int pl,int pr,int j){
	if(l>r||pl>pr) return;int mid=l+r>>1,pos=-1;
	for(int i=pl;i<=min(mid-1,pr);i++){
		if(dp[mid][j]>dp[i][j-1]+calc(i+1,mid))
			dp[mid][j]=dp[i][j-1]+calc(i+1,mid),pos=i;
	} solve(l,mid-1,pl,pos,j);solve(mid+1,r,pos,pr,j);
}
int main(){
	scanf("%d%d",&n,&k);memset(dp,63,sizeof(dp));dp[0][0]=0;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=k;i++) solve(1,n,0,n,i);
	printf("%lld\n",dp[n][k]);
	return 0;
}

2. P5574 [CmdOI2019]任務分配問題

和上面差不多,只不過移指標時要用樹狀陣列統計答案,因此時間複雜度 \(n\log^2n\)

3. LOJ #6039. 「雅禮集訓 2017 Day5」珠寶

首先注意到每件物品的代價很小,最多隻有 \(300\),因此可以考慮在代價上做點文章,記 \(dp_{i,j}\) 表示考慮了代價 \(\le i\) 的物品,總代價 \(\le j\) 的最大價值,再記 \(mx_{i,j}\) 表示代價為 \(i\) 的物品中價值最大的 \(j\) 個物品價值之和,那麼顯然有 \(dp_{i,j}=\max\limits_{j-ki\ge 0}dp_{i-1,j-ki}+mx_{i,k}\),注意到 \(mx_{i,k}\) 為下凸函式,因此該 \(dp\) 轉移方程滿足四邊形不等式,在同一個剩餘類(\(\bmod i\) 的同餘系)中滿足決策單調性,對每個剩餘類分治一下即可。

真·\(10^7\log 10^7\) 給艹過去了

4. P2605 [ZJOI2010]基站選址

此題正解似乎不是什麼決策單調性,但貌似決策單調效能水過去

首先考慮求出每個村莊,要使它不交補償費,顯然必須在某個區間中需有基站,我們假設這個區間為 \([L_i,R_i]\),那麼代價函式 \(w(l,r)\) 的定義即為 \(\sum\limits_{i=l}^rw_i[L_i>l][R_i<r]\),不難發現它滿足四邊形不等式,即 \(w(l,r)+w(l+1,r-1)\ge w(l+1,r)+w(l,r-1)\),分治+決策單調性優化即可,統計答案可用莫隊的思想,加入左/右端點時在對應 vectorlower_bound 即可,理論複雜度 \(\mathcal O(nk\log^2n)\),被正解碾壓,但實際跑起來飛快,吊打部分實現不好的正經寫法。

III. 1D1D:二分佇列

適用範圍:\(dp_i=\min\limits_{j<i}dp_j+w(j+1,i)\)

大概就是,如果某個 \(dp\) 狀態滿足決策單調性,那麼對於某兩個可以轉移到 \(i\) 的決策點 \(j,k(j<k)\),如果 \(j\) 沒有 \(k\) 來得優,那麼隨著 \(i\) 的增大,\(j\) 肯定更沒有 \(k\) 來得優,此時 \(j\) 就沒有用了。那麼考慮以 \(i\) 的增大為時間線,某個決策點 \(j\) 從出生到被幹掉的命運顯然應該是這樣的:開始時不如某些在它前面的決策點,後來不斷變強,逐漸幹掉在它前面的決策點並(有可能)成為最優決策點,在最優決策點保持一段時間後又被它後面的決策點反超。因此考慮維護一個單調佇列,從隊首到隊尾決策點下標單調遞增,同時對 \(i\) 的貢獻單調遞減,那麼顯然隊首元素就是 \(i\) 的最優決策點。當 \(i\) 變為 \(i+1\) 時我們就不斷彈出隊首,直到隊首元素由於佇列第二個元素即可,將某個決策點 \(i\) 加入佇列時,我們就記 \(need(x,y)\) 表示 \(x\) 最早什麼時候能夠比 \(y\) nb,那麼我們比較 \(need(q[tl],q[tl-1])\)\(need(i,q[tl])\),如果 \(i\) 能比 \(q[tl]\) 幹掉 \(q[tl-1]\) 更早地幹掉 \(q[tl]\),那麼 \(q[tl]\) 顯然就是個廢物,彈出佇列即可。\(need(x,y)\) 顯然可以二分求出。

時間複雜度 \(n\log n\)

5. P1912 [NOI2009] 詩人小G

裸的決策單調性優化 \(dp\),打表可以發現代價函式 \(w\) 滿足四邊形不等式,而狀態轉移方程又滿足 1D1D,因此可以使用二分佇列優化,複雜度 \(n\log n\),注意手寫快速冪並使用 long double 避免精度問題!!!

6. P3515 [POI2011]Lightning Conductor

首先式子可以轉化為 \(p\ge a_j-a_i+\sqrt{|i-j|}\),因此我們只需求出 \(f_i=\max\limits_{j}a_j+\sqrt{|i-j|}\),對於 \(j<i\)\(j>i\) 兩部分顯然是對稱的,因此我們只用著眼於一邊即可,注意到這個 \(w(j,i)=\sqrt{|i-j|}\) 滿足四邊形不等式,因為 \(f(x)=\sqrt{x}\) 為上凸函式,二階導數恆為負,因此可以決策單調性優化,上個決策單調性即可。

const int MAXN=5e5;
int n,a[MAXN+5],q[MAXN+5];
double dp1[MAXN+5],dp2[MAXN+5];
double rt[MAXN+5];
double calc(int x,int y){return a[x]+rt[y-x];}
int need(int x,int y){
	int l=x,r=n,p=n+1;
	while(l<=r){
		int mid=l+r>>1;
		if(calc(x,mid)>calc(y,mid)) p=mid,r=mid-1;
		else l=mid+1;
	} return p;
}
void solve(double *dp){
	int hd=1,tl=0;
	for(int i=1;i<=n;i++){
		while(hd<tl&&need(i,q[tl])<=need(q[tl],q[tl-1])) --tl;
		q[++tl]=i;
		while(hd<tl&&calc(q[hd],i)<calc(q[hd+1],i)) ++hd;
		dp[i]=calc(q[hd],i);
	}
}
int main(){
	scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) rt[i]=sqrt(i);
	solve(dp1);reverse(a+1,a+n+1);solve(dp2);reverse(a+1,a+n+1);
	for(int i=1;i<=n;i++) printf("%d\n",max((int)(ceil(max(dp1[i],dp2[n-i+1])))-a[i],0));
	return 0;
}

IV. 二維:記錄決策點

適用範圍:二維 \(dp\),滿足決策單調性。

假設 \(dp_{i,j}\) 的決策點 \(from_{i,j}\) 隨著 \(j\) 的增大而增大,也隨著 \(i\) 的增大而增大,那麼不難發現不等式 \(from_{i,j-1}\le from_{i,j}\le from_{i+1,j}\) 一定成立,因此我們正序列舉 \(j\),倒序列舉 \(i\),然後在對應區間內轉移即可,顯然複雜度是嚴格平方的,因為如果我們把 \(dp\) 看作一個矩形,那麼矩形每條對角線上的決策點都是依次遞增的,列舉次數自然也是 \(\mathcal O(n)\) 的,而對角線個數 \(\mathcal O(n)\),因此總複雜度平方。

7. CF321E Ciel and Gondolas

\(dp_{i,j}\) 表示前 \(i\) 個人劃分成 \(j\) 段的最小代價,那麼顯然代價函式 \(w(l,r)\) 滿足決策單調性,記錄轉移點轉移即可,轉移時就求個二維字首和,複雜度嚴格平方。

using namespace fastio;
const int MAXN=4000;
const int MAXK=800;
int n,k,a[MAXN+5][MAXN+5],s[MAXN+5][MAXN+5];
int dp[MAXN+5][MAXK+5],from[MAXN+5][MAXK+5];
int sum(int l1,int r1,int l2,int r2){
	return s[r1][r2]-s[l1-1][r2]-s[r1][l2-1]+s[l1-1][l2-1];
}
int main(){
	read(n);read(k);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
		read(a[i][j]);s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
	} memset(dp,63,sizeof(dp));dp[0][0]=0;
	for(int i=1;i<=k;i++) for(int j=n;j;j--)
		for(int l=from[j][i-1];l<=((j==n)?n:from[j+1][i]);l++)
			if(l<j&&dp[j][i]>dp[l][i-1]+sum(l+1,j,l+1,j))
				dp[j][i]=dp[l][i-1]+sum(l+1,j,l+1,j),from[j][i]=l;
	printf("%d\n",dp[n][k]>>1);
	return 0;
}

8. P4767 [IOI2000]郵局

同上,唯一的區別是代價函式的計算方式不同&輸入格式不同(大霧)

9. P5897 [IOI2013]wombats

nb tea,線段樹分塊+決策單調性優化 dp,題解

參考資料:

2. wqs 二分

基本思想

似乎跟決策單調性關係不算太大,但是與決策單調性一樣都屬於通過觀察 \(dp\) 陣列的性質來優化 \(dp\),因此就在這裡一併講掉了(

引入:給定一個由正整陣列成的序列 \(a\),你可以把它分成 \(k\) 段,使每段和的平方之和最小。

我會暴力 \(dp\)\(dp_{i,j}\) 表示前 \(i\) 個數分成 \(j\) 段的最小代價,複雜度 \(n^3\)

太慢了!有沒有快一點的做法?

我會決策單調性優化 \(dp\)!這東西的代價函式滿足四邊形不等式,可以分治優化,複雜度 \(nk\log n\)

還是慢了!有沒有再快一點的做法?

我會斜率優化!這東西可以寫成一次函式的形式,斜率優化一下可以到 \(\mathcal O(nk)\)

能不能再快一點!

………………

這時候我們的 wqs 二分就要派上用場了。wqs 二分,又稱忘情水王欽石二分,帶權二分,DP 凸優化,常見於限制選取物品個數的 DP 當中(或者限制劃分成 \(k\) 段),如果我們發現 \(dp_i\) 滿足凹凸性(上凸/下凸),那麼就可以考慮 wqs 二分。

關於 wqs 二分一個比較感性的認識是,以剛才的例子為例,如果我們不限制段數,那麼根據 \((\sum\limits_{i=1}^na_i)^2\ge\sum\limits_{i=1}^na_i^2\) 可知我們肯定會貪心地選擇每個元素單獨一段,這樣可能不滿足 \(k\) 的限制,因此我們考慮加上一個額外的條件:每多分一段就可以 \(-c\) 的代價,那麼顯然如果 \(c\) 趨近於 \(\infty\) 我們就每個元素自己單獨成一段了,而 \(c=0\) 就是之前的情形,那麼我們能不能找到一個分界點,使得在上述情形中最優方案剛好分了 \(k\) 段呢?答案是肯定的,這就是 wqs 二分的基本思想。因此如果做題拿不準是否是 wqs 二分可以用該方法感性地判斷,或者實在不行打個表觀察一下也行。

感性的認識講完了,下面講理性的理解,還是以上面的例子為例,下面簡記 \(f(x)\) 表示上面的 \(dp_{n,x}\),打個表發現 \(f(x)\) 是個凸函式,因此考慮二分一個斜率 \(c\) 並用斜率為 \(-c\) 的直線去截這個凸包,那麼該直線一定會卡到凸包下方的某個點,這個點顯然一定是在 \(y\) 軸上截距最小的點——或者說,就是上面感性認識中,我們給每段代價 \(-c\) 後最優劃分方案,我們考慮求出這個最優劃分方案——這個就可以按照上面暴力做法那樣決策單調性/斜率優化了,同時我們記錄最優方案劃分的段數 \(p\),如果 \(p=k\) 那顯然本題就做完了,答案就是 \(\text{最優劃分策略的答案}+kc\),否則如果 \(p>k\) 那我們就將 \(c\) 調大一點,如果 \(p<k\) 就將 \(c\) 調小一點,這樣本題就做完了,複雜度 \(n\log n/n\log^2n\),吊打上面的三個做法。

重要實現細節:三點共線

上述做法夢想很美滿,現實很骨感,你照著上面的思路寫,過了樣例,一交……

WA……WA?WA!

原因是因為你沒有特殊判斷下面的情形:

在上面的情形中,假如我們的 \(k\) 剛好就是 \(C\) 點的橫座標,那麼在 \(c\) 稍微大一點的情況下會切到 \(B\)(即上圖中的橙色直線 \(k_1\)),\(c\) 稍微小一點的情況下會切到 \(D\)(即上圖中的粉色線段),永遠也輪不到 \(C\),所以上面的狀態還有待改進,我們考慮在最優答案相同時,保留劃分段數最大的答案,然後如果劃分段數 \(\ge k\) 就更新答案,並且更新答案為最終最優解 \(+k\times mid\),而不是最優解 \(+\text{最優劃分段數}\times k\)。為什麼要限制這個劃分段數最大/最小呢,因為顯然直線在切到 \(B\) 和切到 \(D\) 之間總會有一個斜率的分界點對吧,我們的答案肯定會取分界點左邊減去一個很小的值 \(\epsilon\) 對吧,那由於它大於真正的 \(BD\) 之間的斜率 \(k\),因此它總會切到 \(D\),但由於它與真正的分界點之間的差太微小了,我們的程式會把它與 \(B\) 處的答案認作同一個值,那麼怎麼辦呢……這時候就要以劃分段數為第二關鍵字了,如果我們沒有這個操作那切到 \(B\) 時候答案就不會被更新了,這顯然是我們所不希望的,加上這個操作就可以避免這個 error 了。當然你也可以保留劃分段數最小的答案,那麼此時就要在劃分段數 \(\le k\) 時更新答案,這樣就可以過了。

其他細節

  • 在 wqs 二分中,由於 \(dp\) 值一般是整數,因此斜率 \(c=\dfrac{dp_{i+1}-dp_i}{i+1-i}=dp_{i+1}-dp_i\) 也是整數,因此二分可以不用實數域上二分,但如果碰到小數的 \(dp\) 值就要實數域二分了。
  • 可以將 \((\text{代價},\text{劃分段數})\) 封成一個類,手動過載小於號和加號,這樣可以省去不少繁瑣的比較操作,但可能常數會有億點大(因為加了類封裝後常數本來就會變大)

wqs 二分與費用流的關係

眾所周知,

如果是一個費用流模型,它肯定具有下凸性。
因為費用流的過程中,肯定是先增廣最短路,增廣完了之後不可能增廣更短的路。

——某位我不認識的神仙

說人話,就是假設我們每次增廣流量為 \(1\) 的流,那麼顯然每次增廣時我們會選擇一條 \(S\to T\) 的最短路徑,並令答案加上這條路徑的權值之和,由於增廣之後會減少正向邊的權值,因此對於一條 \(S\to T\) 的路徑,如果我們第 \(k\) 次增廣時增廣了它,那麼顯然 \(\forall i\in[1,k-1]\),第 \(i\) 次增廣時這條路徑也在圖上,因此第 \(i\) 次增廣的權值 \(g(i)\) 是一個遞增函式,前 \(i\) 次增廣的路徑之和 \(f(i)\) 自然就是一個下凸函式。所以如果多次增廣的費用流問題,我們可以用 wqs 二分來優化。

例題

10. P2619 [國家集訓隊]Tree I

\(f_i\) 為選 \(i\) 條白邊的答案,那麼我們感性理解一下,如果我們給每條白邊權值 \(-\infty\),那麼顯然選擇的白邊條數會取到最大值;如果給它們都加上 \(\infty\),那麼選擇的白邊條數會取到最小值——也就是說存在一個分界點 \(c\) 使得給每條白邊權值 \(-c\) 後最小生成樹中恰好有 \(k\) 條白邊,故 \(f_i\) 為凸函式,wqs 二分即可,複雜度 \(n\log^2n\),可以使用歸併排序,即將黑邊白邊在預處理時分別排序,每次二分時歸併一下即可,配合路徑壓縮+啟發式合併可以做到 \(\mathcal O(n\alpha(n)\log n)\),沒有興趣實現……

const int MAXN=5e4;
const int MAXM=1e5;
int n,m,need,ans;
struct edge{
	int u,v,w,col;
	bool operator <(const edge &rhs){
		return (w^rhs.w)?(w<rhs.w):(col<rhs.col);
	}
} e[MAXM+5];
int f[MAXN+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
bool check(int mid){
	for(int i=1;i<=n;i++) f[i]=0;
	for(int i=1;i<=m;i++) if(!e[i].col) e[i].w-=mid;
	sort(e+1,e+m+1);int tot=0,res=0;
	for(int i=1;i<=m;i++){
		int fu=find(e[i].u),fv=find(e[i].v);
		if(fu==fv) continue;f[fu]=fv;
		tot+=!e[i].col;res+=e[i].w;
	} for(int i=1;i<=m;i++) if(!e[i].col) e[i].w+=mid;
	return (tot>=need)?(ans=res+mid*need,1):0;
}
int main(){
	scanf("%d%d%d",&n,&m,&need);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].col);
		++e[i].u;++e[i].v;
	} int l=-100,r=100,p=0;
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid)) p=mid,r=mid-1;
		else l=mid+1;
	} printf("%d\n",ans);
	return 0;
}

11. CF739E Gosha is hunting

首先三方的 \(dp\) 顯然:\(dp_{i,a,b}\) 表示取前 \(i\) 個球的過程中中用了 \(a\) 個寶貝球和 \(b\) 個超級球的最小代價,顯然可以 wqs 二分,於是上個 wqs 二分找最優劃分段數為 \(B\) 的斜率即可,時間複雜度平方對數,可以通過 wqs 二分套 wqs 二分做到 \(n\log^2n\),有興趣的可以自己去實現一下,反正我是從來沒有興趣咯……

通過以下程式碼瞭解一下決策單調性 \(dp\) 陣列的類如何實現:

const int MAXN=2000;
const double EPS=1e-12;
int n,x,y;double a[MAXN+5],b[MAXN+5],ans=0;
struct dp_val{
	int num;double res;
	dp_val(int _num=0,double _res=0):num(_num),res(_res){}
	bool operator <(dp_val rhs) const{
		return (fabs(res-rhs.res)<EPS)?(num>rhs.num):(res<rhs.res);
	}
	dp_val operator +(dp_val rhs) const{
		return dp_val(num+rhs.num,res+rhs.res);
	}
} dp[MAXN+5][MAXN+5];
int main(){
	scanf("%d%d%d",&n,&x,&y);
	for(int i=1;i<=n;i++) scanf("%lf",&a[i]);
	for(int i=1;i<=n;i++) scanf("%lf",&b[i]);
	double l=0,r=1;
	while(fabs(r-l)>EPS){
		double mid=1.0*(l+r)/2;
		for(int i=1;i<=n;i++) for(int j=0;j<=y;j++) dp[i][j]=dp_val();
		for(int i=1;i<=n;i++) for(int j=0;j<=y;j++){
			chkmax(dp[i][j],dp[i-1][j]+dp_val(0,0));
			chkmax(dp[i][j],dp[i-1][j]+dp_val(1,a[i]-mid));
			if(j){
				chkmax(dp[i][j],dp[i-1][j-1]+dp_val(0,b[i]));
				chkmax(dp[i][j],dp[i-1][j-1]+dp_val(1,a[i]+b[i]-a[i]*b[i]-mid));
			}
		} dp_val mx;
		for(int j=0;j<=y;j++) chkmax(mx,dp[n][j]);
		if(mx.num<=x) r=mid,ans=mx.res+x*mid;
		else l=mid;
	} printf("%.10lf\n",ans);
	return 0;
}

此外,此題還可以使用費用流求解:新建兩個點 \(A,B\),從 \(S\)\(A\) 連容量 \(a\) 權值 \(0\) 的邊,向 \(B\) 連容量 \(b\) 權值 \(0\) 的邊,從 \(A\) 向每個球 \(i\) 連容 \(1\) 權值 \(p_i\) 的邊;從 \(B\) 向每個球連容 \(1\) 權值 \(u_i\) 的邊,然後從 \(i\)\(T\) 連兩條邊,容量均為 \(1\),一條權值 \(0\),一條權值 \(-u_ip_i\),然後跑最大費用最大流即可。

12. P4383 [八省聯考2018]林克卡特樹

首先題目等價於選擇 \(k+1\) 條不同的鏈,問它們邊權之和的最大值,打個表可以發現該 \(dp\) 為凸函式,因此可以 wqs 二分。

二分一個斜率 \(k\),然後設 \(dp_{u,0/1/2}\) 表示考慮了 \(u\) 的子樹,\(u\) 下方連了 \(0/1/2\) 條邊的最大權值之和,再設 \(f_u\) 表示以 \(u\) 為根的子樹中,與 \(u\) 的父親不連邊時的答案。轉移就列舉對應的兒子的狀態合併一下即可,具體來說轉移方程如下(其中 \(w\)\((u,v)\) 邊的權值):

  • \(dp_{u,2}=\max\{dp_{u,2},dp_{u,1}+dp_{v,1}+w-mid,dp_{u,1}+f_{v}\}\)
  • \(dp_{u,1}=\max\{dp_{u,1},dp_{u,0}+dp_{v,1}+w-mid,dp_{u,0}+f_v\}\)
  • \(dp_{u,0}=dp_{u,0}+dp_{v,0}\)

最後 \(f_u=\max\{dp_{u,0},dp_{u,1}-mid,dp_{u,2}\}\)

注意轉移順序,應按照 \(2\to 1\to 0\) 的順序轉移,還有二分斜率有可能是負的,因此二分下界不能設為 \(0\)!!!!111

13. CF802O April Fools' Problem (hard)

並不是愚人節的題目(大霧

首先此題顯然可以 wqs 二分否則我就不會把它放在這裡了(London Fog

對於每一道題有三種選擇:

  1. 跳過它,不使用它
  2. 把它當作一個準備的決策
  3. 把它和之前最小的 \(a\) 匹配,並列印

我們可以用優先佇列來維護這個過程,時間複雜度 \(n\log n\log w\)

14. CF958E2 Guard Duty (medium)

還是按照套路 wqs 二分,然後二分內部是一個 dp,樸素 dp 是平方的,稍微用點腦子就知道可以使用字首 \(\min\) 優化,複雜度 \(n\log n\)

15. P4983 忘情

首先將題目中那個奇奇怪怪的貢獻柿子拆開可得 \(w(l,r)=(1+\sum\limits_{i=l}^ra_i)^2\),然後按照套路 wqs 二分即可,wqs 二分內部我們設 \(dp_i\) 表示劃分前 \(i\) 個數的最小代價,那麼有 \(dp_i=\sum\limits_{j<i}w(j+1,i)+dp_j\),斜率優化即可,複雜度 \(n\log n\)

16. P6246 [IOI2000] 郵局 加強版

沒錯就是 \(8\) 的加強版,不過學了 wqs 二分這個加強版和原題就沒啥區別了……

上個 wqs 二分,然後決策單調性優化一下即可。

由於轉移方程是 1D1D,因此需要二分佇列

17. P5633 最小度限制生成樹

如果把每個點的顏色看作它是否有一個端點為 \(s\),那麼就跟 \(10\) 一樣了……

注意判 Impossible

18. P5308 [COCI2019] Quiz

上凸殼寫成下凸殼了都能過樣例,就 nm 離譜

首先如果不考慮 \(k\) 的限制那麼可以考慮倒著 \(dp\)\(dp_i\) 表示還剩 \(i\) 個人最多能夠獲得的獎金,那麼有 \(dp_i=\max\limits_{j<i}dp_j+\dfrac{i-j}{i}\),發現這東西可以斜率優化,即對於 \(j<k\),從 \(k\) 轉移比從 \(j\) 轉移更優的充要條件是 \(dp_j+\dfrac{i-j}{i}<dp_k+\dfrac{i-k}{i}\),即 \(\dfrac{dp_j-dp_k}{j-k}>\dfrac{1}{i}\),斜率優化一下即可。加個 \(k\) 的限制之後上個 wqs 即可。

19. P5617 [MtOI2019]不可視境界線 / 2021.7.14 六校聯訓 T2 / NFLSOJ #1055

真·昨天剛學的 wqs 二分今天就能在模擬賽中派上用場

傻逼卡精度+卡常屑題

首先一眼 wqs 二分,二分內部可以 \(dp\)\(dp_i\) 表示前 \(i\) 個圓覆蓋的並減去 \(mid\) 乘圓的個數的最大值,那麼顯然有轉移方程 \(dp_i=\max\limits_{j<i}dp_j+w(j,i)-mid\),其中 \(w(j,i)\) 是一個與 \(x_i-x_j\) 有關的函式,可以通過 cmath 庫預處理出來,發現它是一個下凸函式,因此莽個決策單調性即可。

時間複雜度 \(n\log n\log w\),然鵝由於涉及浮點數的運算,常數上天……

參考資料: