2019.07.10【NOIP提高组】模拟 A 组T1 树的难题

10 篇文章 0 订阅
6 篇文章 0 订阅

树的难题

题目连接

一眼DP,但想不到状态怎么设,在半醒半睡中度过了四十分钟
设f[i,j]表示第i个点,所在集合状态为j时的最小代价

j=0表示有0个黑色点

j=1表示有0个白色点

j=2表示有1个白色点

设a[i]为第i个点颜色黑色为0,白色为1,灰色为2

j为i的儿子,s为i与j之间的边的权值

当a[i]<>0时

f[i,0]为\sum min(f[j,0],f[j,1]+s,f[j,2]+s)

 

当a[i]<>1时

f[i,1]为\sum min(f[j,0]+s,f[j,1],f[j,2]+s)

 

当a[i]=1时,i所在集合中唯一一个白点显然为i

所以与i联通i的儿子的集合都不能有白点

所以f[i,2]为\sum min(f[j,0]+s,f[j,1],f[j,2]+s)

 

如果a[i]不为1,就要从儿子的集合中钦定一个集合有白点与i联通

var
	i1,i,j,n,t,x,y,z,so,ct,kk1:longint;
	op1,op2,kk,ans,maxp:int64;
	h,a,zz,fa:array[1..300000]of longint;
	f:array[1..300000,0..2]of int64;
	g,la,ss:array[1..600000]of longint;
function min(x,y:int64):int64;
begin
	if x<y then exit(x)
	else exit(y);
end;
procedure ad(x,y,z:longint);
begin
	inc(so);
	g[so]:=y;
	la[so]:=h[x];
	ss[so]:=z;
	h[x]:=so;
end;
procedure mt(x:longint);
var
	i:longint;
begin
	i:=h[x];
	while i<>0 do
	begin
		if g[i]<>fa[x] then
		begin
			inc(ct);
			zz[ct]:=g[i];
			fa[g[i]]:=x;
			mt(g[i]);
		end;
		i:=la[i];
	end;
end;
begin
	readln(t);
	maxp:=300000000000000;
	for i1:=1 to t do
	begin
		fillchar(h,sizeof(h),0);
		fillchar(fa,sizeof(fa),0);
		fillchar(f,sizeof(f),0);
		ct:=1;
		so:=0;
		zz[1]:=1;
		readln(n);
		for i:=1 to n do read(a[i]);
		for i:=1 to n-1 do
		begin
			readln(x,y,z);
			ad(x,y,z);
			ad(y,x,z);
		end;
		mt(1);
		for i:=n downto 1 do
		begin
			if a[zz[i]]=0 then f[zz[i],0]:=maxp;
			if a[zz[i]]=1 then f[zz[i],1]:=maxp;
			j:=h[zz[i]];
			kk:=0;
			kk1:=0;
			while j<>0 do
			begin
				if g[j]<>fa[zz[i]] then
				begin
					if a[zz[i]]<>0 then
					begin
						f[zz[i],0]:=f[zz[i],0]+min(f[g[j],0],
						min(f[g[j],1]+ss[j],f[g[j],2]+ss[j]));
					end;
					if a[zz[i]]<>1 then f[zz[i],1]:=f[zz[i],1]+min(f[g[j],1],
						min(f[g[j],0]+ss[j],f[g[j],2]+ss[j]));
					if a[zz[i]]=1 then
					begin
						f[zz[i],2]:=f[zz[i],2]+min(f[g[j],0]+ss[j],
						min(f[g[j],1],f[g[j],2]+ss[j]));
					end
					else
					begin
						if (kk1=0) then
						begin
							kk1:=g[j];
							op1:=min(f[g[j],1],min(f[g[j],0],f[g[j],2])+ss[j]);
						end
						else
						begin
							op2:=min(f[g[j],1],min(f[g[j],0],f[g[j],2])+ss[j]);
							if op2-f[g[j],2]>op1-f[kk1,2] then
							begin
								kk:=kk+op1;
								kk1:=g[j];
								op1:=op2;
							end
							else kk:=kk+op2;
						end;
					end;
				end;
				j:=la[j];
			end;
			if kk1>0 then kk:=kk+f[kk1,2];
			if a[zz[i]]<>1 then f[zz[i],2]:=kk;
		end;
		ans:=min(min(f[1,0],f[1,1]),f[1,2]);
		writeln(ans);
	end;
end.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值