dp凸優化/wps二分學習筆記(洛谷4383 [八省聯考2018]林克卡特樹lct)
qwq
安利一個凸優化講的比較好的部落格
https://www.cnblogs.com/Gloid/p/9433783.html
但是他的暴力部分略微有點問題
qwq
我還是詳細的講一下這個題+這個知識點吧。
還是先從題目入手。
首先我們分析題目。
因為題目要刪除
條邊,然後再新建
條邊,求兩點的路徑和。
那我們不妨這麼考慮,對於新連線一條邊,相當於連結了原樹上的兩條鏈,且鏈不存在交點。
那我們新建
條邊,就相當於把原樹上沒有交的
條鏈連線起來。
既然要求權值最大。
那我們就可以直接把題目轉換成求樹上選出點不相交的 條鏈的最大收益。(每個點都是一條鏈)。
qwq
首先考慮應該怎麼
暴力
由於對於 這顆子樹,要分情況討論他屬於一條鏈的端點,中間點,還是不屬於鏈。
所以我們定義狀態 表示 的子樹裡,已經選了 條鏈,其中 這個點不屬於鏈,屬於一個鏈的端點,屬於一個鏈的中心點的最大收益。
首先,因為負權,所以要把所有的 弄成初始值是 的,其中 為0。
然後我們考慮應該怎麼轉移。
對於一個點來說,我們先列舉他的兒子,然後列舉他子樹內的鏈數
,列舉分配給他的兒子的鏈數
那麼對於不同的
我們需要分情況討論。
對於
來說,他可以從兒子的任意一個狀態轉移過來。
對於
來說,首先他可以由當前狀態的
+兒子的
中最大的那個轉移,表示不與當前的兒子構成鏈。
也可以從當前狀態的0+兒子的1+
,表示從當前兒子上來一條鏈。
另外的一種情況就是隻選與當前兒子相連的邊。
對於2來說,其實也是和1同理。
void solve(int x,int fa)
{
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if(p==fa) continue;
solve(p,x);
for (int j=min(k,size[x]);j>=1;j--)
{
for (int z=0;z<=min(j,size[p]);z++)
{
dp[x][j][2]=max(dp[x][j][2],dp[x][j-z][2]+max(dp[p][z][0],max(dp[p][z][1],dp[p][z][2])));
dp[x][j][2]=max(dp[x][j][2],dp[x][j-z][1]+dp[p][z+1][1]+val[i]);
if (j>z) dp[x][j][2]=max(dp[x][j][2],dp[x][j-z][1]+val[i]+dp[p][z][0]);
//本身就有1一條鏈
dp[x][j][1]=max(dp[x][j][1],dp[x][j-z][1]+max(dp[p][z][0],max(dp[p][z][1],dp[p][z][2])));
//和當前構成一條鏈
dp[x][j][1]=max(dp[x][j][1],dp[x][j-z][0]+dp[p][z][1]+val[i]);
if (j>z) dp[x][j][1]=max(dp[x][j][1],dp[x][j-z-1][0]+dp[p][z][0]+val[i]);
dp[x][j][0]=max(dp[x][j][0],dp[x][j-z][0]+max(dp[p][z][0],max(dp[p][z][1],dp[p][z][2])));
}
}
}
}
這裡有兩個需要注意的問題,首先是
要倒著列舉,因為一個鏈只能被選一次(防止出現舊自己更新新自己的情況)
其次
要迴圈到0(只是用來針對只算一條邊的情況。)
然後通過以上的過程,我們就能輕鬆愉悅的通過
的60分了。
那麼應該怎麼優化這個過程呢。
凸優化
首先,我們通過做差分,即 , 表示 時候的答案,發現斜率是逐漸降低的,那我們不難發現在 一定的情況下,其實 是關於 的上凸函式。(並且一定要滿足斜率單調!!!!!!)、
那我們實際上就是要求出來在
的時候的那個對應的凸包的點是多少。
根據斜率單調
我們可以直接二分一個
,然後讓所有的邊權都加上
,然後不限制選的鏈的條數,求最大收益和鏈數,這個複雜度是
的。
因為我們發現,當 的時候,一定是選 條,當 的時候,是選0條,那麼因為斜率單調(所以選擇的條數一定是隨著mid單調變大的),所以我們一定能通過改變這個 ,存在某一個 滿足恰好選擇 條,那麼正好符合題目要求。
另外一種理解方式。
qwq這個理解方式,每次要減去
,其實本質是一樣的。
我們相當於對於每個點求出他的正上方所對應的凸包的點是啥,那麼我們通過
這個東西,相當於把原圖的上的每個點
向下移動
,那麼我們可以發現,隨著
的變大,每次隨便選的取到的收益的最高的地方,是單調右移的。我們只需要記錄一下選的最大收益和鏈數,然後找到鏈數等於
所對應的
,計算一遍貢獻就行。
至於如何做不限制條數的收益,和 類似的
感覺這個東西要感性+理性啊
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 3e5+1e2;
const int maxm = 2*maxn;
const int inf = 1e12;
int point[maxn],nxt[maxm],to[maxm],val[maxm];
int n,m,cnt;
int l,r,ans,k;
struct ymh{
int val,num;
ymh operator + (ymh b)
{
return (ymh){val+b.val,num+b.num};
}
};
ymh max(ymh a,ymh b)
{
if(a.val==b.val)
{
if(a.num<b