P5024 保衛王國 倍增 dp

2022-04-30 21:24:18 字數 3947 閱讀 2019

窩當然不會ddp啦,要寫這題當然是考慮優化裸dp啦,但是這題非常麻煩,於是變成了黑題。

首先,這個是沒有上司的舞會模型,求圖的帶權最大獨立集。

不考慮國王的限制條件,有

\[dp[x][0]+=dp[y][1]\\

dp[x][1]+=min(dp[y][1],dp[y][0])

\]現在考慮限制條件,如果對每乙個限制條件都做一次dp,複雜度達到\(o(n^2)\),無法承受。

顯然,對於這些限制條件,每一次的變動不會影響其它大多數的狀態。

對於乙個限制條件,我們分開考慮,先考慮只對乙個城市進行限制的情況。

若該城市被要求駐紮軍隊,那麼如果在最優情況下它本來就需要軍隊,則沒有影響;如果它本來不需要軍隊,由於此時所有子樹都是最優解,那麼它只會對從它到根節點的路徑的最優解產生影響

若該城市被要求不能駐紮,那麼與上面的情況類似,如果它本來需要駐紮,那麼會對它到根節點的路徑造成影響。

綜上所述,我們是否可以考慮對於每個限制條件,只對兩個點到根節點之間的路徑的某些狀態進行修改呢?答案是肯定的。

假設待修改節點為\(x\)。首先,此時除了\(x\sim root\)的路徑上的節點所表示的狀態,其它狀態都是最優的。對於這條路徑的更新,實際上就是又對這條路徑做了一次dp,並強制\(x\)選或不選,從而限制轉移。

顯然這是可行的,但是複雜度仍為\(o(n^2)\),我們需要一些手段進行優化。

容易發現我們更新的是一條鏈,對於一條鏈,我們自然可以想到用倍增或者樹剖(它們維護的都是樹鏈)來處理每個點的轉移。

以倍增為例。

首先考慮裸dp的步驟,乙個乙個跳,每次從兒子轉移。那我們能不能一段一段地跳,每次從已有資訊中轉移呢?當然可以。

先考慮只有乙個點的情況。

如果我們要對\(x\)進行限制,那麼我們不妨把整個圖變成這樣,方便分析

其中綠色的部分是\(x\)的子樹的最優解。

顯然我們在修改乙個點之後,造成的對原最優解的影響是一定的,產生的新最優解是一定的,這意味著我們可以通過一些手段預處理出來。

不妨考慮通過倍增預處理出最優情況下每個點產生變動(選變成不選,不選變成選)在這條\(x\sim root\)的鏈上產生的新最優解,也就是預處理出修改後dp時跳某一段的該段最優解。

然後從\(x\)樹上倍增跳到\(root\),合併所得的新的最優解,就是這次限制條件下的答案。

實際上,我們的倍增預處理是在一次裸dp的基礎上進行的。換句話說,倍增時用到的資訊就是每個點表示的狀態的原來的最優解,然後加乙個限制條件,再做乙個dp。

形象的講,這個倍增預處理,就是dp套dp。

那這個倍增怎麼做呢?

考慮狀態的刻畫,顯然由幾個較小的子問題合併成較大的子問題時,這些較小的子問題不能有交叉,否則無法基於二進位制劃分合併成更大的子問題。再者,子問題必須覆蓋整個狀態空間。也就是說,在狀態定義時,我們要不重不漏。

既然如此,對於第一點,不難想到倍增時我們不僅要記錄\(x\)的狀態(0/1),還要記錄它跳\(2^k\)步的祖先的狀態。

對於第二點,不難想出狀態包含的範圍。

設\(f[0/1][0/1][k][x]\)表示\(x\)節點變為0不可選、1可選時,向上\(2^k\)步的祖先\(y\)為0可選、1不可選時,\(y\)除去\(x\)以及\(x\)的子樹的原最優解的其它所有子樹的新最優解

如下圖,假設節點3是\(x\)的\(2^k\)祖先,狀態為紅色部分

而3的\(2^k\)祖先(假設是\(root\))表示的狀態就是藍色部分。

這樣就可以很好的合併狀態,之後統計答案的過程中,我們只需列舉\(x\)的狀態並倍增跳即可。

對於節點\(y\),它的父節點為\(x\),有

\[f[1][0][0][y]=dp[0][x]-dp[1][y]\\ f[0][1][0][y]=f[1][1][0][y]=dp[1][x]-min(dp[1][y],dp[0][y])

\]得到轉移(cao)

\[f[0][0][j][y]=min(f[0][0][j-1][y]+f[0][0][j-1][fa],f[0][1][j-1][y]+f[1][0][j-1][fa])\\

f[0][1][j][y]=min(f[0][0][j-1][y]+f[0][1][j-1][fa],f[0][1][j-1][y]+f[1][1][j-1][fa])\\

f[1][0][j][y]=min(f[1][0][j-1][y]+f[0][0][j-1][fa],f[1][1][j-1][y]+f[1][0][j-1][fa])\\

f[1][1][j][y]=min(f[1][0][j-1][y]+f[0][1][j-1][fa],f[1][1][j-1][y]+f[1][1][j-1][fa])

\]然後就是統計答案,當然是倍增統計答案。

從待修改點的兩種不同狀態開始往上倍增合併新最優解,直到根節點。

對於兩個待修改點\(x,y\)的情況,我們像求lca一樣,先統計\(x\sim lca(x,y),y\sim lca(x,y)\),再統計\(lca(x,y)\sim root\)即可。

注意乙個細節,當\(x,y\)兩點跳到\(lca\)的兒子處我們需要同時減去它們兩個的原最優解,再往上跳。

複雜度降至\(o(nlogn)\)。

樹剖也是乙個道理,預處理變動後最優解。

#include#include#include#include#include#include#include#include#include#define inf 0x3f3f3f3f

#define pi acos(-1.0)

#define n 100010

#define mod 2520

#define e 1e-12

#define ll long long

using namespace std;

inline ll read()

while(c>='0'&&c<='9')

return x*f;

}struct recg[n<<1];

int head[n],tot,n,m;

ll dp[2][n],f[2][2][21][n],gi[21][n],t,dep[n],w[n];

inline void add(int x,int y)

inline void dfs(int x,int fa)

} if(x==y) lca=x,b?l1=x1:l0=x0;

else

} lca=gi[0][x];

l0=dp[0][lca]-dp[1][x]-dp[1][y]+x1+y1;

l1=dp[1][lca]-min(dp[0][x],dp[1][x])-min(dp[0][y],dp[1][y])+min(x1,x0)+min(y1,y0);

} if(lca==1) ans=min(l0,l1);

else

} ans=min(dp[0][1]-dp[1][lca]+l1,dp[1][1]-min(dp[0][lca],dp[1][lca])+min(l1,l0));

} return ans>op;

t=log2(n)+1;

for(int i=1;i<=n;++i) w[i]=read();

for(int i=1;i

dfs(1,0);

init();

while(m--)

printf("%lld\n",get(x,a,y,b));

} return 0;

}

洛谷 P5024 保衛王國 (倍增)

很多人寫了題解了,我就懶得寫了,推薦一篇部落格 那就分享一下我的理解吧 說得好像有人看一樣 對於每個點都只有選與不選兩種情況,所以直接用倍增預處理出來兩種情況的子樹之內,子樹之外的最值,最終答案以拼湊的方式得出 如果這個題要修改權值的話就真的只能用動態dp了 好像還有那個什麼全域性平衡樹 我真的覺得...

LG5024 保衛王國

給定一棵 n 個點的樹,每個點的代價為 a i 每條邊至少有乙個端點要被選。m 次詢問,規定 x 和 y 選或不選,求覆蓋整棵樹的最小權值。考場上拿掉 44pts 就放棄了 下文中的 u 為子節點。這是不是非常顯然 dp x 0 1 表示第 x 個點不選 選時的最小代價 每次詢問時把 dp x 1 ...

Luogu5024 保衛王國

f 表示以 u 為根的子樹中,u 不取的最小開銷 f 表示以 u 為根的子樹中,u 必取的最小開銷 g 表示以 u 為根的子樹中,不取重兒子,且 u 不取的最小開銷 g 表示以 u 為根的子樹中,不取重兒子,且 u 必取的最小開銷 設 u 的重兒子為 w f g f f g min f f 轉化方程...