“蔚来杯“2022牛客暑期多校训练营5 D题: Birds in the tree

D题: Birds in the tree

原题链接:https://ac.nowcoder.com/acm/contest/33190/D

题目大意

给定包含 n n n 个节点的树,每个节点具有颜色 0 0 0 或颜色 1 1 1 。求其有多少联通子图,满足度数为 1 1 1 的节点颜色相同。

题解

方便起见,将题目中的树视为一颗有根树。
s x s_x sx 表示节点 x x x 的儿子, c x c_x cx 表示节点 x x x 的颜色。

给出一个定义:
若一张联通子图满足以下条件:

  • 节点全部在以节点 x x x 为根的子树内。
  • 包含节点 x x x
  • 子图中除 x x x 以外的度数为 1 1 1 的节点的颜色均为 c c c

则称该子图为节点 x x x c c c 可并子图。
特别的,我们认为 { x } \{x\} {x} 是节点 x x x c c c 可并子图当且仅当 c = c x c=c_x c=cx

举例:

{ 1 , 2 , 6 } \{1,2,6\} {1,2,6} { 1 , 2 , 3 , 4 , 6 } \{1,2,3,4,6\} {1,2,3,4,6} , { 1 , 2 } \{1,2\} {1,2} 均为节点 1 1 1 的紫可并子图。
{ 1 } \{1\} {1} (子树根 1 1 1 颜色不为紫), { 2 , 3 , 4 } \{2,3,4\} {2,3,4} (不包含子树根 1 1 1 ), { 1 , 2 , 3 , 5 } \{1,2,3,5\} {1,2,3,5} (除子树根 1 1 1 外,节点 5 5 5 的度数为 1 1 1 但颜色非紫)不是节点 1 1 1 的紫可并子图。

不难发现,合法的联通子图一定是可并子图,但可并子图不一定是合法的连通子图。
当两张可并子图的根连接后,一定会形成一张合法的连通子图(同时也是新的可并子图)。

我们设 d p x , c dp_{x,c} dpx,c 表示节点 x x x c c c 可并子图数量。
转移时,对于每个儿子可以选取一个可并子图( d p s x , c dp_{s_x,c} dpsx,c 种)或不选( 1 1 1 种),根据乘法原理相乘即可,不过注意减去全部儿子都不选且子树根颜色不同的情况。
可得转移式:
d p x , c = ∏ s x ( d p s x , c + 1 ) − [ c x = = c ] dp_{x,c}=\prod_{s_x}(dp_{s_x,c}+1)-[c_x==c] dpx,c=sx(dpsx,c+1)[cx==c]

([ f f f ]当 f f f 为真时值为 1 1 1 f f f 为假时值为 0 0 0 )

最终答案计算时,用 全部可并子图数量 减去 可并子图不合法的数量(不合法当且仅当只选择一颗子树(即子树根度数为 1 1 1 )且 c x ≠ c c_x\ne c cx=c (即子树根颜色不同)) 即可。
a n s = ∑ c ( ∑ x d p x , c − ∑ s x d p s x , c ∗ [ c ! = c x ] ) ans=\sum_c(\sum_xdp_{x,c}-\sum_{s_x}dp_{s_x,c}*[c!=c_x]) ans=c(xdpx,csxdpsx,c[c!=cx])

参考代码

#include<bits/stdc++.h>
using namespace std;

template<class T>inline void read(T&x){
	char c,last=' ';
	while(!isdigit(c=getchar()))last=c;
	x=c^48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c^48);
	if(last=='-')x=-x;
}

const int MAXN=3e5+5,P=1e9+7;
int n;
int c[MAXN];
vector<int>e[MAXN],s[MAXN];
int dp[MAXN][2];

void _dfs(int u,int fa){//处理出儿子 
	for(int v:e[u]){
		if(v==fa)continue;
		s[u].push_back(v);
		_dfs(v,u);
	}
}

void dfs(int u){
	dp[u][0]=dp[u][1]=1;
	for(int v:s[u]){
		dfs(v);
		for(int C=0;C<2;++C){
			dp[u][C]=1ll*dp[u][C]*(1+dp[v][C])%P;//合并儿子的可并子图
		}
	}
	dp[u][c[u]^1]=(1ll*dp[u][c[u]^1]-1+P)%P;//减去子树根异色的情况,注意取模前+P 
}

int main()
{
	read(n);
	string str;cin>>str;str=" "+str;
	for(int i=1;i<=n;++i)c[i]=str[i]-'0';
	for(int i=1,u,v;i<n;++i){
		read(u),read(v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	_dfs(1,0);
	dfs(1);
	long long ans=0;
	for(int C=0;C<2;++C){
		for(int u=1;u<=n;++u){
			ans=(ans+dp[u][C])%P;//可并子图数
			if(c[u]!=C){//若子树根异色则非法,减去
				for(int v:s[u]){
					ans=(ans-dp[v][C]+P)%P;
				}
			}
		}
	}
	cout<<ans<<'\n';
	return 0;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值