BZOJ4712洪水 動態DP 樹鏈剖分 線段樹

2022-05-08 03:15:10 字數 2977 閱讀 3728

小a走到乙個山腳下,準備給自己造乙個小屋。這時候,小a的朋友(op,又叫管理員)開啟了創造模式,然後飛到

山頂放了格水。於是小a面前出現了乙個瀑布。作為平民的小a只好老實巴交地爬山堵水。那麼問題來了:我們把這

個瀑布看成是乙個n個節點的樹,每個節點有權值(爬上去的代價)。小a要選擇一些節點,以其權值和作為代價將

這些點刪除(堵上),使得根節點與所有葉子結點不連通。問最小代價。不過到這還沒結束。小a的朋友覺得這樣

子太便宜小a了,於是他還會不斷地修改地形,使得某個節點的權值發生變化。不過到這還沒結束。小a覺得朋友做

得太絕了,於是放棄了分離所有葉子節點的方案。取而代之的是,每次他只要在某個子樹中(和子樹之外的點完全

無關)。於是他找到你。

輸入檔案第一行包含乙個數n,表示樹的大小。

接下來一行包含n個數,表示第i個點的權值。

接下來n-1行每行包含兩個數fr,to。表示書中有一條邊(fr,to)。

接下來一行乙個整數,表示操作的個數。

接下來m行每行表示乙個操作,若該行第乙個數為q,則表示詢問操作,後面跟乙個引數x,表示對應子樹的根;若

為c,則表示修改操作,後面接兩個引數x,to,表示將點x的權值加上to。

n<=200000,保證任意to都為非負數

對於每次詢問操作,輸出對應的答案,答案之間用換行隔開。

44 3 2 1

1 21 3

4 24

q 1q 2

c 4 10

q 1314

考慮沒有修改的情況:

我們可以樹形$dp$,$f[i]$表示堵住以$i$為根的子樹的最小代價,顯然可以得到轉移方程$f[i]=min(val[i],\sum f[to])$其中$val[i]$表示刪除$i$點的代價,$to$表示$i$的子節點。我們設$g[i]$表示點$i$所有輕兒子的$f$之和,那麼$f[i]=min(val[i],g[i]+f[son[i]])$其中$son[i]$為$i$的重兒子。我們將後面的$f[son[i]]$展開,那麼$f[i]=min(val[i],g[i]+min(val[son[i]],g[son[i]]+f[son[son[i]]]))$。可以發現$f[i]$的最小值就是從$i$開始的連續一段重鏈的$g$值$+$這段重鏈鏈尾的$val$值。我們用圖更形象地說明:

其中第一行為重鏈,下面為他們各自的輕兒子(乙個點的所有輕兒子用乙個點代表)。

那麼最小值就是一段$g$與乙個$val$的和的最小值。這個東西實際上就是固定左端點的最小連續子段和(我們稱之為最小連續左端和)。

我們樹鏈剖分後用線段樹維護每條重鏈的最小連續左端和即可。

現在考慮有修改操作的情況:

假設修改點為$x$,那麼首先會影響$x$所在重鏈的最小連續左端和即$x$所在重鏈鏈頭(假設為$u$)的$f$值,也就會進一步影響$u$的父節點的$g$值及$u$的父節點所在重鏈的最小連續左端和,以此類推會影響到根節點。我們從$x$節點開始往根方向修改沿途重鏈上的點維護的資訊即可。查詢時直接查詢以查詢點為左端點的最小連續左端和。注意只有修改$x$時修改的是$val$值,其他被修改的點都修改的是$g$值。

#include#include#include#include#include#include#include#include#include#include#include#define ll long long

using namespace std;

ll v[200010];

ll g[200010];

ll f[200010];

int tot;

int n,m;

int head[200010];

int next[400010];

int to[400010];

int son[200010];

int top[200010];

int fa[200010];

int bot[200010];

int size[200010];

int s[200010];

int pos[200010];

int q[200010];

int num;

int x,y;

ll z;

char ch[3];

struct miku

t[800010];

void add(int x,int y)

void dfs(int x)}}

}void dfs2(int x,int tp)

for(int i=head[x];i;i=next[i])

}if(!son[x])

else

}void pushup(int rt)

void build(int rt,int l,int r)

int mid=(l+r)>>1;

build(rt<<1,l,mid);

build(rt<<1|1,mid+1,r);

pushup(rt);

}void updata(int x)

}miku query(int rt,int l,int r,int l,int r)

miku ls,rs,res;

int mid=(l+r)>>1;

if(l>mid)

else if(r<=mid)

else

}void change(int x,ll val)

else

val=query(1,1,n,s[top[x]],s[bot[x]]).mn-cnt;

x=fa[top[x]];

}}int main()

for(int i=1;i

dfs(1);

dfs2(1,1);

build(1,1,n);

scanf("%d",&m);

for(int i=1;i<=m;i++)

else}}

bzoj4712 洪水 樹鏈剖分 動態dp

我們首先想乙個dp方程 f u mi nf u min val u f u m in。這個方程可以通過矩陣的形式來表示。先把樹輕重鏈剖分了。我們設g u g u g u 為u uu的所有輕兒子的f ff總和,v vv為u uu的重兒子。則f u mi nf u min f u m in。我們可以把這...

BZOJ4712 洪水 樹鏈剖分維護Dp

小a走到乙個山腳下,準備給自己造乙個小屋。這時候,小a的朋友 op,又叫管理員 開啟了創造模式,然後飛到 山頂放了格水。於是小a面前出現了乙個瀑布。作為平民的小a只好老實巴交地爬山堵水。那麼問題來了 我們把這 個瀑布看成是乙個n個節點的樹,每個節點有權值 爬上去的代價 小a要選擇一些節點,以其權值和...

BZOJ4712 洪水 樹鏈剖分維護Dp

小a走到乙個山腳下,準備給自己造乙個小屋。這時候,小a的朋友 op,又叫管理員 開啟了創造模式,然後飛到 山頂放了格水。於是小a面前出現了乙個瀑布。作為平民的小a只好老實巴交地爬山堵水。那麼問題來了 我們把這 個瀑布看成是乙個n個節點的樹,每個節點有權值 爬上去的代價 小a要選擇一些節點,以其權值和...