[ZJOI2007]时态同步(树形DP+DFS)

P1131 [ZJOI2007]时态同步
题目描述
小Q在电子工艺实习课上学习焊接电路板。一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字1,2,3….进行标号。电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点,都存在且仅存在一条通路(通路指连接两个元件的导线序列)。
在电路板上存在一个特殊的元件称为“激发器”。当激发器工作后,产生一个激励电流,通过导线传向每一个它所连接的节点。而中间节点接收到激励电流后,得到信息,并将该激励电流传向与它连接并且尚未接收到激励电流的节点。最终,激烈电流将到达一些“终止节点”――接收激励电流之后不再转发的节点。
激励电流在导线上的传播是需要花费时间的,对于每条边e,激励电流通过它需要的时间为te,而节点接收到激励电流后的转发可以认为是在瞬间完成的。现在这块电路板要求每一个“终止节点”同时得到激励电路――即保持时态同步。由于当前的构造并不符合时态同步的要求,故需要通过改变连接线的构造。目前小Q有一个道具,使用一次该道具,可以使得激励电流通过某条连接导线的时间增加一个单位。请问小Q最少使用多少次道具才可使得所有的“终止节点”时态同步?
输入输出格式
输入格式:

第一行包含一个正整数N,表示电路板中节点的个数。
第二行包含一个整数S,为该电路板的激发器的编号。
接下来N-1行,每行三个整数a , b , t。表示该条导线连接节点a与节点b,且激励电流通过这条导线需要t个单位时间。
输出格式:

仅包含一个整数V,为小Q最少使用的道具次数。
输入输出样例
输入样例#1:

3
1
1 2 1
1 3 3
输出样例#1:

2
说明
对于40%的数据,N ≤ 1000
对于100%的数据,N ≤ 500000
对于所有的数据,te ≤ 1000000
思路:
这个题正解思路是树形DP,我的思路是两遍深度优先搜索(其实也是树形DP,只是累加的方式不同,但实质上就是树形DP)。
思路是这样的,对于这一棵树,我们执行第一遍DFS的时候是要调整好树上所有节点的属性,包括深度,节点到根节点的前缀和距离(因为后面的查询边权我是利用的前缀和搞的)这些。然后第二遍DFS是核心,首先递推到叶子节点,然后对于每一个有儿子节点的节点,我们比较它们的出度路径长度并找出最大值,以及它们的路径条数,既然每一个道具都是增加一个单位,我们还要尽可能的让它少用道具,那我们对每一个节点的所有出边的操作就是分别让经过这些子节点到达叶子节点的路径长度一致。而我们只能增加长度,所以我们要求一个最大值,让这些不是最大值的边向最大值靠拢,而所需要的代价就是最大值*出边数(使用道具以后的总时间)-出边边权总值(当前的总时间),对于每一次的代价用一个ANS来记录即可。
切记long long,不然会有两个点是WA了的,改了好几次TAT。

#include<iostream> 
#include<cstdio> 
#include<cstdlib> 
using namespace std;
long long i,j,m,n,s; 
long long hd[1000001],temp; 
long long ANS; 
long long deep[1000001],dis[1000001]; 
long long dismax; 
long long sum[1000001]; 
long long ma[1000001]; 
struct data 
{ 
long long v,t,nxt; 
}a[2000001]; 
long long r() 
{ 
long long ans=0; 
char ch=getchar(); 
while(ch<'0'||ch>'9') 
{ 
ch=getchar();
} 
while(ch>='0'&&ch<='9') 
{
ans*=10; 
ans+=ch-'0'; 
ch=getchar(); 
} 
return ans; 
} 
void add(long long x,long long y,long long z) 
{ 
a[++temp].v=y;
a[temp].t=z; 
a[temp].nxt=hd[x]; 
hd[x]=temp; 
} 
void dfs(long long x) 
{ 
for(long long p=hd[x];p;p=a[p].nxt) 
{ 
if(!deep[a[p].v]) 
{ 
deep[a[p].v]=deep[x]+1; 
dis[a[p].v]=dis[x]+a[p].t;
dfs(a[p].v); 
} 
} 
} 
void dfs2(long long x) 
{ 
long long mm=0,t=0; 
for(long long p=hd[x];p;p=a[p].nxt) 
{ 
if(deep[a[p].v]>deep[x])
{ 
dfs2(a[p].v); 
if(dis[ma[a[p].v]]-dis[x]>mm) 
{ 
ma[x]=ma[a[p].v];
mm=dis[ma[a[p].v]]-dis[x]; 
} 
sum[x]+=dis[ma[a[p].v]]-dis[x];
t++; 
} 
} 
ANS+=(dis[ma[x]]-dis[x])*t-sum[x]; 
} 
int main() 
{ 
n=r(); 
s=r(); 
long long xx,yy,zz; 
for(i=1;i<n;i++) 
{ 
xx=r(),yy=r(),zz=r(); 
add(xx,yy,zz); 
add(yy,xx,zz); 
} 
temp=0; 
deep[s]=1; 
dfs(s); 
for(i=1;i<=n;i++) 
{
dismax=max(dismax,dis[i]); 
ma[i]=i;
} 
dfs2(s); 
cout<<ANS; 
return 0; 
}
 /* 7 1 1 2 3 1 3 1 1 4 7 3 5 5 3 6 1 6 7 2 */

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值