E - Tree Painting 小G砍树(换根dp、vector、auto、树dfs)

E. Tree Painting

题意:给你一个图,每次选一个点涂成黑色,顺便获得权值(该节点的树上白色点的数目),每次只能选择与黑色相邻的白色节点涂色,求所有操作之后的权值和

思路:以每个节点开始涂色的权值和为定值,则遍历所有节点为根节点的情况的权值,取最大值则为答案,dp求每种根的情况的答案d[i]=d[son]+sz[i],sz[i]为树的节点数,dfs所有子节点情况并根转移,d[u]~d[v]转化

知识点:换根dp、vector、auto、树dfs

#include<bits/stdc++.h>
using namespace std;
const int N=200005;
vector<int> vec[N];//动态数组存子节点 
long long int d[N];//保存每结点子树的权值总和  即 题目所求 
	int n,sz[N];//保存该子树的节点数 
long long int ans=0;
void dfs(int u,int fa)//求1节点的权值 
{
	sz[u]=1;//每个子树的节点数初始化为1 
	for(auto v : vec[u])//用 v 表示 子节点 
	{
		if(v==fa) continue;//dfs体现 如果 u 的子节点 v 为 u 的父节点 fa 
						//   (fa已经涂色) 则跳过此子节点 
		dfs(v,u);//求子节点的权值d[v] 和 子树节点数sz[v]
		sz[u]+=sz[v];//把每一个子树大小相加 
		d[u]+=d[v];//该点子节点权值 相加 
		
	}
	d[u]+=sz[u];//dp体现  该点权值 加  所有子节点的权值 
	
}
void dfs1(int u,int fa)
{
	ans=max(ans,d[u]);//在u节点为第一次涂色情况时的权值大小 ,取大值更新ans 
	for( auto v: vec[u]) //换根 
	{
		if(v==fa) continue;//dfs体现 父节点已经算过 跳过 
		long long temp=d[u]-sz[v]-d[v];//p体现 
		//d[1]=d[2]+d[4]+1+sz[2]+sz[4]  ->  d[4]+1+sz[4]
		//即 d[1]=d[1]-d[2]-sz[2]  ->temp=d[u]= d[u]-d[v]-sz[v]							 
		d[v]=d[v]+(n-sz[v])+temp;//p体现  
		//d[2]=d[3]+d[5]+d[6]+1+sz[3]+sz[5]+sz[6] -> d[3]+d[5]+d[6] ( +d[1] / +temp )+1+sz[3]+sz[5]+sz[6]+(n-sz[1])
		dfs1(v,u);//继续向子节点扫描 
	}
}
int main()
{
	cin>>n;
	for(int i=0;i<n-1;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		vec[x].push_back(y);
		vec[y].push_back(x);
		//记录每两个节点的联系 
	}
	dfs(1,0);//求出1号节点的权值 初始化 
	dfs1(1,0);//换根 
	cout<<ans<<endl;
}

小G砍树

链接:https://ac.nowcoder.com/acm/contest/375/C
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给你一棵n个节点的带标号无根树。每次,你可以选择一个度数为1的节点并将它从树上移除。问总共有多少种不同的方式能将这棵树删到只剩 1 个点。两种方式不同当且仅当至少有一步被删除的节点不同。

输入描述:

第一行一个数n。接下来n-1行,描述这棵树的n-1条边。节点编号为1~n。

输出描述:

一行一个正整数,表示方案数对998244353取模的值。

示例1

输入

复制

4
1 2
1 3
1 4

输出

复制

12

备注:

n≤100000n≤100000

题意:求不同根的情况时,排列树的和

思路:求出d[1]根为1时的排列数,d[1]=n!/sz[1~n](排列消序),然后换根d[v]转换d[v]=d[u]*(sz[u]/sz`[u])*(sz[v]/sz`[v]) 其中sz[u]=n,sz`[v]=n,sz`[u]=n-sz[v];化简后:d[v]=d[u]*sz[v]/(n-sz[v]))第一次dfs求出sz[1~n],d[1]=n!/sz[1~n],第二次dfs换根求d[2~n],ans+=d[1~n],输出ans即答案。

核心:dfs 全排列

#include<bits/stdc++.h> 
using namespace std;
#define mod 998244353
const int N=100005;
vector<int> vect[100005];
long long int n,sz[N],d[N],ans=0;
void dfs(int u,int fa)
{
	sz[u]+=1;
	for(auto z : vect[u])
	{
		if(z==fa) continue;	 
		dfs(z,u);
		sz[u]=(sz[u]+sz[z])%mod;	
	}
}
void dfs2(int u,int fa)
{
	long long int niyuan(long long int a,long long int b);
	ans=(ans+d[u])%mod;
	for(auto v: vect[u])
	{
		if(v==fa) continue;
		d[v]=niyuan((d[u]*sz[v])%mod,n-sz[v]);
		dfs2(v,u);
	}
}
long long int niyuan(long long int a,long long int b)
{
	long long int qiuck(int c);
	return a*qiuck(b)%mod;
}
long long int qiuck(int b)//快速幂 
{
	int y=mod-2;
	long long int bb=1;
	long long int x=b;
	while(y)
	{
		if(y&1) bb=(bb*x)%(mod);
		x=(x*x)%(mod);
		y>>=1;
	}
	return bb;
}
int main()
{
	cin>>n;
	long long cj=1,cjj=1;
	for(int i=0;i<n-1;i++)
	{
		long long int x,y;
		scanf("%lld%lld",&x,&y);
		vect[x].push_back(y);
		vect[y].push_back(x);
		cj=(cj*(i+2))%mod;
		
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)
	{
		cjj=((cjj%mod)*(sz[i])%mod)%mod;
	}
	d[1]=niyuan(cj,cjj);//逆元 
	dfs2(1,0);
	cout<<ans<<endl;
	
}
发布了44 篇原创文章 · 获赞 6 · 访问量 1212

猜你喜欢

转载自blog.csdn.net/qq_43868883/article/details/95196532