Description
给定一个 个节点的树。现在你拥有 种颜色,你要用这些颜色给树上的每个节点染色,使得任何两个距离不大于 的不同节点所被染的颜色不同。
由于答案可能过大,请将其对 取模。
Solution
不管各位大佬有多强一眼看穿这题,但是本蒟蒻没有这个本事。于是,我从宏观想,一直向下剖析,最终得到了正解。
于是,这篇题解将会变得格外详细。
Part 1
这是经典的染色问题。
设 表示第 个节点所能被染的颜色的数量。因为这是一棵树,所以答案就是 。
于是,难点转到了如何正确地得到所有的 。
Part 2
容易发现,一个父节点的任何两个子节点的距离均为2。
为了方便叙述,这里把"父节点"设为 号节点。同时,设我们在 号节点的孩子中,我们第 个填了 号节点。
那么显然有 。
原因如下: 填了 号节点之后,它使 与 所能被填的颜色均少了一种;填了 号节点之后,它使 能被填的颜色又少了一种。
因此,总有 。所以只要我们得到 的值,那么接下来的 的值就全得到啦。
于是,最后的难点转到了求 的值。
Part 3
由于 是在 号节点的孩子中第一个被填的,所以它不会被其他的 号节点的孩子所影响。
但是,它仍然会被与它距离不超过2的祖先所影响。即,如果它有父亲,那么它能填的颜色就少了一种;如果它有爷爷,那么它能填的颜色就又少了一种,注意其父亲的颜色与其爷爷的颜色一定不同。
综上所述,
①当 时, ( 为所能够填的颜色的数量);
②当 时,由于它没有爷爷,那么
③当 时,由于既有爷爷也有父亲,那么 。
得到了 的值之后呢,我们使用Part 2的做法就能得到所有 的值。最后求出 就可以啦。
注意取模。
评定
①难度: 不会评
②算法: 数学,数论+树形结构+搜索+深度优先搜索,dfs
③时间复杂度:
④评价: 这是一道染色问题的入门题,树形结构的普通题,也是一道让我想了 分钟的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];
强迫症地存下了每条边,然后用链式前向星存图。
注意这里要初始化 ,因为要做乘法。
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;
}
注意:
①加两次边,否则WA;
②一边乘一边取模,否则溢出;
③个人认为可能会出现节点没有颜色可涂,这样该节点能涂的颜色就是0或负数,所以说 格外重要。不要以为不会出现这样的情况——当该树只有两层且 小于 。这时应该输出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;
}
撒花✿✿ヽ(°▽°)ノ✿撒花