BZOJ 3257 或 JZOJ 3347. 【NOI2013模拟】树的难题

H y p e r l i n k Hyperlink Hyperlink

https://www.lydsy.com/JudgeOnline/problem.php?id=3257


D e s c r i p t i o n Description Description

给定一个含有 n n n个节点的树,每个节点有自己的颜色(黑白灰),每条相连的边都有自己的权值

定义一棵树满足以下条件即为一棵合法的树:

  1. 不含黑点
  2. 至多有一个白点

你可以选择花费一条边的权值的代价来割除这条边

求将该树变成一片合法的森林(即割出来的树也必须合法)的最小修改代价

n ≤ 3 × 1 0 5 n\leq 3\times 10^5 n3×105


S o l u t i o n Solution Solution

把1看成根,然后设方程 f [ i ] [ 0 / 1 / 2 ] f[i][0/1/2] f[i][0/1/2]表示以 i i i为根,以这个节点为根的子树无黑点/无白点/有一个白点的最小代价,转移显然

需要注意的是,由于 G M O J GMOJ GMOJ(即 J Z O J JZOJ JZOJ)很垃圾,所以要用拓扑序来 d p dp dp,否则会爆栈。

时间复杂度: O ( n ) O(n) O(n)


C o d e Code Code

#include
#include
#include
#include
#define N 300010
using namespace std;int n,a[N],tot,l[N],d[N],q[N],fa[N];
long long f[N][3];
struct node{int next,to,w;}e[N<<1];
inline void add(int u,int v,int w){e[++tot]=(node){l[u],v,w};l[u]=tot;return;}
inline long long read()
{
    char c;int f=0,d=1;
    while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
    return d*f;
}
inline void dfs()
{
	int head=1,tail=0;q[++tail]=1;
	while(head<=tail)
	{
		int x=q[head++];
		for(register int i=l[x],y;i;i=e[i].next) 
		{
			y=e[i].to;
			if(y==fa[x]) continue;
			fa[y]=x;q[++tail]=y;d[y]=e[i].w;
		}
	}
	for(register int i=tail;i>0;i--)
	{
		int x=q[i];
		if(a[x]==0)
		{
			f[x][0]=1LL<<50LL;
			long long ans1=0,ans2=0;
			for(register int i=l[x];i;i=e[i].next)
			{
				int y=e[i].to;
				if(y==fa[x]) continue;
				ans1+=min(f[y][1],min(f[y][0],f[y][2])+d[y]);
			}
			f[x][1]=ans1;
			ans2=ans1;
			for(register int i=l[x];i;i=e[i].next)
			{
				int y=e[i].to;
				if(y==fa[x]) continue;
				ans2=min(ans2,ans1-min(f[y][1],min(f[y][0],f[y][2])+d[y])+f[y][2]);
			}
			f[x][2]=ans2;
		}
		if(a[x]==1)
		{
			f[x][1]=1LL<<50LL;
			long long ans0=0,ans2=0;
			for(register int i=l[x];i;i=e[i].next)
			{
				int y=e[i].to;
				if(y==fa[x]) continue;
				ans0+=min(f[y][0],min(f[y][1],f[y][2])+d[y]);
				ans2+=min(f[y][1],min(f[y][0],f[y][2])+d[y]);
			}
			f[x][0]=ans0;
			f[x][2]=ans2;
		}
		if(a[x]==2)
		{
			long long ans0=0,ans1=0,ans2=0;
			for(register int i=l[x];i;i=e[i].next)
			{
				int y=e[i].to;
				if(y==fa[x]) continue;
				ans0+=min(f[y][0],min(f[y][1],f[y][2])+d[y]);
				ans1+=min(f[y][1],min(f[y][0],f[y][2])+d[y]);
			}
			ans2=ans1;
			for(register int i=l[x];i;i=e[i].next)
			{
				int y=e[i].to;
				if(y==fa[x]) continue;
				ans2=min(ans2,ans1-min(f[y][1],min(f[y][0],f[y][2])+d[y])+f[y][2]);
			}
			f[x][0]=ans0;
			f[x][1]=ans1;
			f[x][2]=ans2;
		}
	}
	return;
}
signed main()
{
	for(int t=read();t--;tot=0)
	{
		memset(l,0,sizeof(l));
		n=read();
		for(register int i=1;i<=n;i++) a[i]=read(),f[i][0]=f[i][1]=f[i][2]=1LL<<50LL;
		for(register int i=1,u,v,w;i<n;i++)
		{
			u=read();v=read();w=read();
			add(u,v,w);add(v,u,w);
		}
		d[1]=0;dfs();
		printf("%lld\n",min(f[1][0],min(f[1][1],f[1][2])));
	}
}

你可能感兴趣的:(dp,树形dp)