B a r n P a i n t i n g Barn\ Painting Barn Painting
题目链接: j z o j 6657 jzoj\ 6657 jzoj 6657 / l u o g u 4084 luogu\ 4084 luogu 4084
题目
给定一颗 N N N个节点组成的树, 3 3 3种颜色,其中 K K K个节点已染色,要求任意两相邻节点颜色不同,求合法染色方案数。
输入
第
1
1
1行:
N
N
N和
K
K
K
第
2
~
N
2~N
2~N行:
x
x
x和
y
y
y代表点
x
x
x和点
y
y
y
第
N
+
1
~
N
+
K
N+1~N+K
N+1~N+K行:
b
b
b和
c
c
c代表点
b
b
b已经被涂上了颜色
c
c
c
输出
合法的染色方案数 % 1 0 9 + 7 \%10^9+7 %109+7
样例输入
4 1
1 2
1 3
1 4
4 3
样例输出
8
数据范围
1
≤
N
≤
1
0
5
1≤N≤10^5
1≤N≤105,
0
≤
K
≤
N
0≤K≤N
0≤K≤N
1
≤
x
,
y
≤
N
1≤x,y≤N
1≤x,y≤N,
x
≠
y
x≠y
x=y
1
≤
b
≤
N
1≤b≤N
1≤b≤N,
1
≤
c
≤
3
1≤c≤3
1≤c≤3
思路
这道题是一道树形
d
p
dp
dp。
我们设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为在点
i
i
i涂颜色
j
j
j的方案数,那么首先初始化,我们把所有点涂所有颜色的方案数都是
1
1
1。
接着,我们来处理数据中直接涂了颜色的点。这个也很好做,就是把这个点涂了的颜色的方案数标为
1
1
1,剩下的两个颜色方案数标为
0
0
0。
接着我们就要开始
d
p
dp
dp,就按照与一个点相邻的点不能跟它是同一个颜色的原则,我们就可以写出
d
p
dp
dp式。
注意:
- 要开 l o n g l o n g long\ long long long
- 要记得取模
代码
#include<cstdio>
#include<cstring>
#define mo 1000000007
#define ll long long
using namespace std;
struct node{
int to, next;
}e[200001];
int n, k, x, y, kk, le[200001];
ll f[200001][4];
void add(int x, int y) {//连边
e[++kk] = (node){y, le[x]}; le[x] = kk;
}
void dfs(int now, int fa) {//树形dp
for (int i = le[now]; i; i = e[i].next)
if (e[i].to != fa) {
dfs(e[i].to, now);
f[now][1] = f[now][1] * ((f[e[i].to][2] + f[e[i].to][3]) % mo) % mo;//三个dp式,分别表示涂这三个颜色的方案数
f[now][2] = f[now][2] * ((f[e[i].to][1] + f[e[i].to][3]) % mo) % mo;
f[now][3] = f[now][3] * ((f[e[i].to][1] + f[e[i].to][2]) % mo) % mo;
}
}
int read() {//快读
int re = 0, zf = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c = '-') zf = -zf;
c = getchar();
}
while (c >= '0' && c <= '9') {
re = re * 10 + c - 48;
c = getchar();
}
return re * zf;
}
int main() {
// freopen("barnpainting.in", "r", stdin);
// freopen("barnpainting.out", "w", stdout);
n = read();//读入
k = read();//读入
for (int i = 1; i <= n; i++)
f[i][1] = f[i][2] = f[i][3] = 1;//初始化
for (int i = 1; i < n; i++) {
x = read();//读入
y = read();//读入
add(x, y);//连边
add(y, x);//两个方向又要连边
}
for (int i = 1; i <= k; i++) {
x = read();//读入
y = read();//读入
for (int i = 1; i <= 3; i++)
f[x][i] = 0;//如果一个点已经涂色,那么这个点就不可能涂其他颜色
f[x][y] = 1;//就只能涂这个颜色
}
dfs(1, 0);//树形dp
printf("%lld", (f[1][1] + f[1][2] + f[1][3]) % mo);//输出答案
// fclose(stdin);
// fclose(stdout);
return 0;
}