codeforces1388C dfs+条件判定+逆向思维

1388C 1800的题

题意:

一个国家由n个城市组成,并且这些城市之间的道路形成了一棵树,并且首都为1,其他城市为2~n,有m个人在首都打工,要回到自己的家乡,这些人有开心的也有不开心的,开心的人也有可能在回家的路上变为不开心的,现在给你数组p[]表示每个城市是多少个人的家乡,h[]表示有多少人经过这个城市是开心的抑或不开心的,数值为开心的人数减去不开心的人数,注意这里是经过即可,在这个城市驻留的也算,然后给你n-1条边,表示这个国家各个城市之间的道路关系,问你这个开心的数值是否合理。

思路:

对于每个城市来说我们可以记录他的人流量,即有多少个人会经过这个城市或者在这个城市驻留,同时对于每个人的心情可以进行计算假设这个城市为i,那么经过这个城市的人为这个城市i的子树的所有的加起来,同时有h[i]=good[i]-bad[i](开心的人数减去不开心的人数),sum=good[i]+bad[i],(sum表示人流量),通过这两个式子可以得到h[i]+sum=good[i]*2。当然对于这个节点对应的子树要dfs一遍才能得到。之后就是对于一些条件的判断了,例如sum+h[i]=good[i]*2,即表示能被2整除,还有good[i]必须小于当前节点的流量,而且大于子树的开心人数。
代码如下:

ll sum[maxn], h[maxn], p[maxn], good[maxn];
bool flag;
std::vector<ll> v[maxn];
inline void dfs(int pre, int u){
	ll s = 0;
	sum[u] = p[u];
	for(int i = 0; i < v[u].size(); i++){
		if(v[u][i]==pre) continue;
		dfs(u, v[u][i]);
		s += good[v[u][i]];
		sum[u] += sum[v[u][i]];
	}
	if((h[u]+sum[u])%2!=0) flag = 0;
	good[u] = (h[u]+sum[u])/2;
	if(good[u]>sum[u]) flag = 0;
	if(good[u]<s) flag = 0;
}
inline void cf(){
	int t;
	t = read();
	while(t--){
		memset(v,0,sizeof(v));
		memset(good, 0, sizeof(good));
		flag = 1;
		int n=read(), m=read();
		for(int i = 1; i <= n; i++) p[i]=read();
		for(int i = 1; i <= n; i++) h[i]=read();
		for(int i = 1; i < n; i++){
			int x=read(), y=read();
			v[x].push_back(y); v[y].push_back(x);
		}
		dfs(0, 1);
		if(flag) printf("YES\n");
		else printf("NO\n");
	}
	return ;
}
signed main(){
	cf();
	return 0;
}

还有一种代码,虽然说思想是差不多的

std::vector<int> v[maxn];
int p[maxn], h[maxn];
int flag;
inline void dfs(int x, int pre){
	ll sum = 0;
	for(int i = 0; i < v[x].size(); i++){
		if(v[x][i]!=pre){
			dfs(v[x][i], x);
			sum += h[v[x][i]];		//当前节点的子树开心的总人数,不包括当前节点
			p[x] += p[v[x][i]];		//当前节点的经过的人数,包括住在这个节点的人,也可以称之为流量把
		}
	}
	h[x]+=p[x];		
	//h[x]/2为当前节点有多少人是以开心的心情经过这个节点的,
	//所以要符合必须是2的倍数
	//同时h[x]/2必须小于等于当前节点的人流量,这个原因简单,假设所有的人经过这个节点都是开心的h[x]/2的最大值也就只能达到p[x]
	//当前节点的开心人数要大于等于其子树的开心人数,因为如果不开心的话不能变为开心
	if(h[x]%2!=0) flag = 0;
	else if(h[x]>2*p[x]) flag = 0;
	else if(h[x]<sum) flag = 0;
}
inline void cf(){
	int t;
	t = read();
	while(t--){
		memset(v,0,sizeof(v));
		flag = 1;
		int n=read(), m=read();
		for(int i = 1; i <= n; i++) p[i]=read();
		for(int i = 1; i <= n; i++) h[i]=read();
		for(int i = 1; i < n; i++){
			int x=read(), y=read();
			v[x].push_back(y);v[y].push_back(x);
		}
		dfs(1,0);
		if(flag) printf("YES\n");
		else printf("NO\n");
	}
	return ;
}
signed main(){
	cf();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值