小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 longusing 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要選擇一些節點,以其權值和...