给定一个 n n n个节点的树。现在你拥有 k k k种颜色,你要用这些颜色给树上的每个节点染色,使得任何两个距离不大于 2 2 2的不同节点所被染的颜色不同。
由于答案可能过大,请将其对 1 0 9 + 7 10^9+7 109+7取模。
不管各位大佬有多强一眼看穿这题,但是本蒟蒻没有这个本事。于是,我从宏观想,一直向下剖析,最终得到了正解。
于是,这篇题解将会变得格外详细。
这是经典的染色问题。
设 a i a_i ai表示第 i i i个节点所能被染的颜色的数量。因为这是一棵树,所以答案就是 ∏ i = 1 n a i ∏_{i=1}^n a_i ∏i=1nai。
于是,难点转到了如何正确地得到所有的 a i a_i ai。
容易发现,一个父节点的任何两个子节点的距离均为2。
为了方便叙述,这里把"父节点"设为 f f f号节点。同时,设我们在 f f f号节点的孩子中,我们第 k k k个填了 s k s_k sk号节点。
那么显然有 a s 1 = a s 2 + 1 = a s 3 + 2 a_{s_1}=a_{s_2}+1=a_{s_3}+2 as1=as2+1=as3+2。
原因如下: 填了 s 1 s_1 s1号节点之后,它使 s 2 s_2 s2与 s 3 s_3 s3所能被填的颜色均少了一种;填了 s 2 s_2 s2号节点之后,它使 s 3 s_3 s3能被填的颜色又少了一种。
因此,总有 a s m = a s m + 1 + 1 a_{s_m}=a_{s_{m+1}}+1 asm=asm+1+1。所以只要我们得到 a s 1 a_{s_1} as1的值,那么接下来的 a s 2 , a s 3 … … a_{s_2}, a_{s_3}…… as2,as3……的值就全得到啦。
于是,最后的难点转到了求 a s 1 a_{s_1} as1的值。
由于 a s 1 a_{s_1} as1是在 f f f号节点的孩子中第一个被填的,所以它不会被其他的 f f f号节点的孩子所影响。
但是,它仍然会被与它距离不超过2的祖先所影响。即,如果它有父亲,那么它能填的颜色就少了一种;如果它有爷爷,那么它能填的颜色就又少了一种,注意其父亲的颜色与其爷爷的颜色一定不同。
综上所述,
①当 d e p t h s 1 = 1 depth_{s_1}=1 depths1=1时, a s 1 = k a_{s_1}=k as1=k ( k k k为所能够填的颜色的数量);
②当 d e p t h s 1 = 2 depth_{s_1}=2 depths1=2时,由于它没有爷爷,那么 a s 1 = k − 1 a_{s_1}=k-1 as1=k−1
③当 d e p t h s 1 = 3 depth_{s_1}=3 depths1=3时,由于既有爷爷也有父亲,那么 a s 1 = k − 2 a_{s_1}=k-2 as1=k−2。
得到了 a s 1 a_{s_1} as1的值之后呢,我们使用Part 2的做法就能得到所有 a i a_i ai的值。最后求出 ∏ i = 1 n a i ∏_{i=1}^n a_i ∏i=1nai就可以啦。
注意取模。
①难度: 不会评
②算法: 数学,数论+树形结构+搜索+深度优先搜索,dfs
③时间复杂度: O ( n ) O(n) O(n)
④评价: 这是一道染色问题的入门题,树形结构的普通题,也是一道让我想了 20 20 20分钟的E题(我太弱了)。欢迎各位大佬轻喷哦!
#include
#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 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 ai的值。
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) max(ai,0)格外重要。不要以为不会出现这样的情况——当该树只有两层且 n n n小于 k k k。这时应该输出0而不是一个负数。
主函数总归还是亲切的,相信大家能理解吧。
最后放上高清无码的代码~
#include
#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;
}
撒花✿✿ヽ(°▽°)ノ✿撒花