Atcoder beginner contest(ABC) 133 E题解

Description

给定一个 n n 个节点的树。现在你拥有 k k 种颜色,你要用这些颜色给树上的每个节点染色,使得任何两个距离不大于 2 2 不同节点所被染的颜色不同。

由于答案可能过大,请将其对 1 0 9 + 7 10^9+7 取模。

Solution

不管各位大佬有多强一眼看穿这题,但是本蒟蒻没有这个本事。于是,我从宏观想,一直向下剖析,最终得到了正解。

于是,这篇题解将会变得格外详细

Part 1

这是经典的染色问题。

a i a_i 表示第 i i 个节点所能被染的颜色的数量。因为这是一棵树,所以答案就是 i = 1 n a i ∏_{i=1}^n a_i

于是,难点转到了如何正确地得到所有的 a i a_i

Part 2

容易发现,一个父节点的任何两个子节点的距离均为2。

为了方便叙述,这里把"父节点"设为 f f 号节点。同时,设我们在 f f 号节点的孩子中,我们第 k k 个填了 s k s_k 号节点。

在这里插入图片描述

那么显然有 a s 1 = a s 2 + 1 = a s 3 + 2 a_{s_1}=a_{s_2}+1=a_{s_3}+2

原因如下: 填了 s 1 s_1 号节点之后,它使 s 2 s_2 s 3 s_3 所能被填的颜色均少了一种;填了 s 2 s_2 号节点之后,它使 s 3 s_3 能被填的颜色又少了一种。

因此,总有 a s m = a s m + 1 + 1 a_{s_m}=a_{s_{m+1}}+1 。所以只要我们得到 a s 1 a_{s_1} 的值,那么接下来的 a s 2 , a s 3 a_{s_2}, a_{s_3}…… 的值就全得到啦。

于是,最后的难点转到了求 a s 1 a_{s_1} 的值。

Part 3

由于 a s 1 a_{s_1} 是在 f f 号节点的孩子中第一个被填的,所以它不会被其他的 f f 号节点的孩子所影响

但是,它仍然会被与它距离不超过2的祖先所影响。即,如果它有父亲,那么它能填的颜色就少了一种;如果它有爷爷,那么它能填的颜色就又少了一种,注意其父亲的颜色与其爷爷的颜色一定不同。

综上所述,

①当 d e p t h s 1 = 1 depth_{s_1}=1 时, a s 1 = k a_{s_1}=k ( k k 为所能够填的颜色的数量);

②当 d e p t h s 1 = 2 depth_{s_1}=2 时,由于它没有爷爷,那么 a s 1 = k 1 a_{s_1}=k-1

③当 d e p t h s 1 = 3 depth_{s_1}=3 时,由于既有爷爷也有父亲,那么 a s 1 = k 2 a_{s_1}=k-2

得到了 a s 1 a_{s_1} 的值之后呢,我们使用Part 2的做法就能得到所有 a i a_i 的值。最后求出 i = 1 n a i ∏_{i=1}^n a_i 就可以啦。

注意取模。

评定

①难度: 不会评

②算法: 数学,数论+树形结构+搜索+深度优先搜索,dfs

③时间复杂度: O ( n ) O(n)

④评价: 这是一道染色问题的入门题,树形结构的普通题,也是一道让我想了 20 20 分钟的E题(我太弱了)。欢迎各位大佬轻喷哦!

代码详解

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;

那熟悉的几行字,不再解释。

int n,k,cnt=0,ans=1;
int head[200005],depth[100005],a[100005];

struct node
{
	int u,v;
}tmp[100005];

struct edge
{
	int next;
	int to;
}e[200005];

强迫症地存下了每条边,然后用链式前向星存图。

注意这里要初始化 a n s = 1 ans=1 ,因为要做乘法。

inline void add_edge(int u,int v)
{
	cnt++;
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}

加边函数~

inline void dfs(int now,int fath)
{
	depth[now]=depth[fath]+1;
	for (int i=head[now];i;i=e[i].next)
	{
		if (e[i].to!=fath)  dfs(e[i].to,now);
	}
}

求出每个点的深度,以便分类讨论。

inline void dfs2(int now,int fath)
{
	if (depth[now]==2)  a[now]--;
	else if (depth[now]>=3)  a[now]-=2;
	
	int flag=0,last;
	for (int i=head[now];i;i=e[i].next)
	{
		if (e[i].to!=fath)
		{
			if (flag==0)
			{
				last=a[e[i].to];
				flag=1;
				continue;
			}
			a[e[i].to]=last-1;
			last=a[e[i].to];
		}
	}
	for (int i=head[now];i;i=e[i].next)
	{
		if (e[i].to!=fath)  dfs2(e[i].to,now);
	}
}

求出所有 a i a_i 的值。

signed main()
{
	cin>>n>>k;
	for (int i=1;i<n;i++)  cin>>tmp[i].u>>tmp[i].v;
	for (int i=1;i<=n;i++)  a[i]=k;
	for (int i=n;i>=1;i--)
	{
		add_edge(tmp[i].u,tmp[i].v);
		add_edge(tmp[i].v,tmp[i].u);
	}
	dfs(1,0);
	dfs2(1,0);
	
	for (int i=1;i<=n;i++)
	{
		int gx=max(a[i],0ll);
		ans=(ans*gx)%mod;
	}
	cout<<ans<<endl;
	
	return 0;
}

注意:

①加两次边,否则WA;

②一边乘一边取模,否则溢出;

③个人认为可能会出现节点没有颜色可涂,这样该节点能涂的颜色就是0或负数,所以说 m a x ( a i , 0 ) max(a_i,0) 格外重要。不要以为不会出现这样的情况——当该树只有两层且 n n 小于 k k 。这时应该输出0而不是一个负数。

主函数总归还是亲切的,相信大家能理解吧。

最后放上高清无码的代码~

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;

int n,k,cnt=0,ans=1;
int head[200005],depth[100005],a[100005];

struct node
{
	int u,v;
}tmp[100005];

struct edge
{
	int next;
	int to;
}e[200005];

inline void add_edge(int u,int v)
{
	cnt++;
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}

inline void dfs(int now,int fath)
{
	depth[now]=depth[fath]+1;
	for (int i=head[now];i;i=e[i].next)
	{
		if (e[i].to!=fath)  dfs(e[i].to,now);
	}
}

inline void dfs2(int now,int fath)
{
	if (depth[now]==2)  a[now]--;
	else if (depth[now]>=3)  a[now]-=2;
	
	int flag=0,last;
	for (int i=head[now];i;i=e[i].next)
	{
		if (e[i].to!=fath)
		{
			if (flag==0)
			{
				last=a[e[i].to];
				flag=1;
				continue;
			}
			a[e[i].to]=last-1;
			last=a[e[i].to];
		}
	}
	for (int i=head[now];i;i=e[i].next)
	{
		if (e[i].to!=fath)  dfs2(e[i].to,now);
	}
}

signed main()
{
	cin>>n>>k;
	for (int i=1;i<n;i++)  cin>>tmp[i].u>>tmp[i].v;
	for (int i=1;i<=n;i++)  a[i]=k;
	for (int i=n;i>=1;i--)
	{
		add_edge(tmp[i].u,tmp[i].v);
		add_edge(tmp[i].v,tmp[i].u);
	}
	dfs(1,0);
	dfs2(1,0);
	
	for (int i=1;i<=n;i++)
	{
		int gx=max(a[i],0ll);
		ans=(ans*gx)%mod;
	}
	cout<<ans<<endl;
	
	return 0;
}

撒花✿✿ヽ(°▽°)ノ✿撒花

原创文章 19 获赞 27 访问量 1943

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/105886250
今日推荐