[USACO17DEC]Barn Painting

题目概况

题意翻译

题意:给定一颗\(N\)个节点组成的树,3种颜色,其中K个节点已染色,要求任意两相邻节点颜色不同,求合法染色方案数。

题目描述

Farmer John has a large farm with \(N\) barns (\(1 \le N \le 10^5\)), some of which are already painted and some not yet painted. Farmer John wants to paint these remaining barns so that all the barns are painted, but he only has three paint colors available. Moreover, his prize cow Bessie becomes confused if two barns that are directly reachable from one another are the same color, so he wants to make sure this situation does not happen. It is guaranteed that the connections between the \(N\) barns do not form any 'cycles'. That is, between any two barns, there is at most one sequence of connections that will lead from one to the other. How many ways can Farmer John paint the remaining yet-uncolored barns?

输入输出格式

输入格式

The first line contains two integers \(N\) and \(K\) (\(0 \le K \le N\)), respectively the number of barns on the farm and the number of barns that have already been painted. The next \(N-1\) lines each contain two integers \(x\) and \(y\) (\(1 \le x, y \le N, x \neq y\)) describing a path directly connecting barns \(x\) and \(y\). The next \(K\) lines each contain two integers \(b\) and \(c\) (\(1 \le b \le N\), \(1 \le c \le 3\)) indicating that barn \(b\) is painted with color \(c\).

输出格式

Compute the number of valid ways to paint the remaining barns, modulo \(10^9 + 7\), such that no two barns which are directly connected are the same color.

输入输出样例

输入样例 #1
4 1
1 2
1 3
1 4
4 3
输出样例 #1
8

解题报告

题意解析

  1. 给定一颗\(N\)个节点组成的树
  2. 每个节点上可以\(3\)种颜色
  3. 其中K个节点染色
  4. 要求任意两相邻节点颜色不同,求合法染色方案数。

算法解析

我们分析一下题目给出的信息

  1. 一棵树,且树的节点很多
  2. 上下关系明显,要求颜色不同
  3. 统计合法方案数量

综上所述,我们不难发现是一道树形动态规划的题目。

然后我们需要设计状态表示。

我们发现,对于一个节点,它的染色,我们需要关心,同时题目让我们求出合法方案数量

不难设计出状态表示为

\[f[i][j],i节点,染色为j,节点i为根的合法方案数量 \]

然后,我们通过题目限制条件,来推到转移方程。

  1. 上下颜色不同
  2. 有些点颜色固定

我们发现,假如说,这个点的颜色是\(1\),那么我们可以发现

\[f[i][1]=f[i][1] \times (f[y1][2]+f[y1][3]) \quad y1是x的儿子节点 \]

首先我们要保证颜色不同,所以\(j=1\),那么儿子节点的颜色就不能为\(1\)

其次,一个点的方案数量,显然等于各个儿子节点,不同染色的方案数量乘积。

  1. 乘法原理,所以是各个儿子方案乘积
  2. 儿子的颜色不等于父亲颜色,但是儿子之间颜色没有限制

所以我们不难推出,状态转移方程为:

\[f[i][1]= \prod(f[y][2]+f[y][3]) \\\\ f[i][2]= \prod(f[y][1]+f[y][3]) \\\\ f[i][3]= \prod(f[y][1]+f[y][2]) \\\\ y为i的儿子节点 \]

接下来,针对一个点颜色固定,我们可以这么处理。

\[若i节点颜色为1 \Rightarrow f[i][2]=f[i][3]=0,f[i][1]=1 \\ 其他同理,只不过修改j的问题而言 \]


代码解析

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+20,Mod=1e9+7;
int n,a[N],k,c[N],x,y;
long long f[N][4];
vector<int> g[N];
inline void Dp(int x,int s)//s为x父亲节点
{
	if (c[x])//颜色限制
		f[x][c[x]]=1;
	else
		f[x][1]=f[x][2]=f[x][3]=1;
	for(int y:g[x])
	{
		if (y==s)
			continue;
		Dp(y,x);
		f[x][1]=f[x][1]*(f[y][2]+f[y][3])%Mod;//当前元素为k,则儿子的颜色不为k
		f[x][2]=f[x][2]*(f[y][1]+f[y][3])%Mod;//因此这个儿子,提供的方案数就为另外两种颜色的之和
		f[x][3]=f[x][3]*(f[y][1]+f[y][2])%Mod;//根据乘法定理,所有儿子的乘积就是我们所谓的当前节点的方案数
	}
}
inline void init()
{
	scanf("%d%d",&n,&k);
	for(int i=1; i<n; i++)
	{
		scanf("%d%d",&x,&y);
		g[x].push_back(y),g[y].push_back(x);
	}
	for (int i=1; i<=k; i++)
	{
		scanf("%d%d",&x,&y);
		c[x]=y;
	}
	Dp(1,0);
	printf("%lld\n",(f[1][1]+f[1][2]+f[1][3])%Mod);
}
signed main()
{
	init();
	return 0;
}
posted @ 2019-11-10 21:05  秦淮岸灯火阑珊  阅读(241)  评论(0编辑  收藏  举报