1. 程式人生 > >[bzoj4712]洪水_動態dp

[bzoj4712]洪水_動態dp

洪水 bzoj-4712

題目大意:給定一棵$n$個節點的有根樹。每次詢問以一棵節點為根的子樹內,選取一些節點使得這個被詢問的節點包含的葉子節點都有一個父親被選中,求最小權值。支援單點修改。

註釋:$1\le n\le 2\cdot 10^5$。保證任意時刻所有節點的權值為正整數。


想法:顯然這是一道動態$dp$的題。

如果沒有單點修改操作,我們直接樹形$dp$:

  狀態:$f_i$表示以$i$為根的答案。

  轉移:$f_i=max(a_i,\sum\limits f_j)$。

現在有了單點修改操作,我們像正常的動態樹形dp一樣樹剖。

維護$g_i=\sum\limits f_j$其中,$j$是$i$的虛兒子。

這樣的話$f_i=max(a_i,g_i+f_{i+1})$因為重兒子在樹剖中緊挨著父親。

進而我們考慮一條重鏈上:因為每個節點的權值都是正的,所以一個葉子節點只有一個祖先會被選中。

我們考慮當前節點所在重鏈的鏈底(顯然是一個葉子),重鏈上一定有且僅有一個節點被選中,不妨設為$x$,那麼代價就是$a_x+\sum\limits_{j=i}^{x-1} g_j$。

就是最小字首和的形式。

所以詢問就是詢問一條重鏈上的最小字首和。

接下來考慮單點修改:

顯然單點修改不會影響重鏈上的$g$值,只需要線上段樹上單點修改即可。

它只會影響每個重鏈的父親。那麼我們求出了當前重鏈鏈頭的$f$值,緊接著更新重鏈鏈頭的父親的$g$,以此類推。

用線段樹維護最小字首和,同時需要維護區間和。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010 
#define lson l,mid,x<<1
#define rson mid+1,r,x<<1|1
using namespace std; typedef long long ll;
struct Node
{
	ll sum,ls;
	Node() {}
	Node(ll g,ll v){sum=g; ls=v;}
	inline Node operator +(const Node &a) const
	{
		return Node(sum+a.sum,min(sum+a.ls,ls));
	}
}a[N<<2],w[N];
int head[N],to[N<<1],nxt[N<<1],cnt,fa[N],size[N],top[N],down[N],dic[N],tot,n;
ll v[N],f[N],g[N];
char str[5];
inline void add(int x,int y) {to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt;}
void dfs1(int x)
{
	size[x]=1;
	for(int i=head[x];i;i=nxt[i]) if(to[i]!=fa[x])
		fa[to[i]]=x,dfs1(to[i]),size[x]+=size[to[i]];
}
void dfs2(int x,int c)
{
	int k=0;
	top[x]=c,dic[x]=++tot,down[x]=x,f[x]=v[x];
	for(int i=head[x];i;i=nxt[i]) if(to[i]!=fa[x]&&size[to[i]]>size[k])
	{
		k=to[i];
	}
	if(k)
	{
		dfs2(k,c),down[x]=down[k];
		for(int i=head[x];i;i=nxt[i])
			if(to[i]!=fa[x]&&to[i]!=k)
				dfs2(to[i],to[i]),g[x]+=f[to[i]];
		f[x]=min(f[x],f[k]+g[x]);
	}
	w[dic[x]]=Node(g[x],v[x]);
}
inline void pushup(int x) {a[x]=a[x<<1]+a[x<<1|1];}
void build(int l,int r,int x)
{
	if(l==r)
	{
		a[x]=w[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lson); build(rson);
	pushup(x);
}
void updatev(int p,ll v,int l,int r,int x)
{
	if(l==r)
	{
		a[x].ls+=v;
		return;
	}
	int mid=(l+r)>>1;
	if(p<=mid) updatev(p,v,lson);
	else updatev(p,v,rson);
	pushup(x);
}
void updateg(int p,ll g,int l,int r,int x)
{
	if(l==r)
	{
		a[x].sum+=g;
		return;
	}
	int mid=(l+r)>>1;
	if(p<=mid) updateg(p,g,lson);
	else updateg(p,g,rson);
	pushup(x);
}
Node query(int b,int e,int l,int r,int x)
{
	if(b<=l&&r<=e) return a[x];
	int mid=(l+r)>>1;
	if(e<=mid) return query(b,e,lson);
	else if(b>mid) return query(b,e,rson);
	else return query(b,e,lson)+query(b,e,rson);
}
inline void modify(int x,ll v)
{
	int y=x;
	ll t;
	while(x)
	{
		t=query(dic[top[x]],dic[down[x]],1,n,1).ls;
		if(x==y) updatev(dic[x],v,1,n,1);
		else updateg(dic[x],v,1,n,1);
		v=query(dic[top[x]],dic[down[x]],1,n,1).ls-t,x=fa[top[x]];
	}
}
int main()
{
	int m,x,y;
	ll z;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&v[i]);
	for(int i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
	dfs1(1),dfs2(1,1);
	build(1,n,1);
	scanf("%d",&m);
	while(m--)
	{
		scanf("%s%d",str,&x);
		if(str[0]=='C')scanf("%lld",&z),modify(x,z);
		else printf("%lld\n",query(dic[x],dic[down[x]],1,n,1).ls);
	}
	return 0;
}

小結:無。