- 题目
- 题解(12)
- 讨论(5)
- 排行
较难 通过率:38.07% 时间限制:1秒 空间限制:1024M
知识点动态规划

校招时部分企业笔试将禁止编程题跳出页面,为提前适应,练习时请使用在线自测,而非本地IDE。
描述
对于给定的由 nn 个节点组成的无根树,每一条边都可以被染上颜色,初始时,全部边均为白色。
现在,选中树上 kk 个不同的点,并将它们标记,随后,定义,如果一条树边 (u,v)(u,v) 满足节点 uu 和 vv 同时被标记,那么这条树边自动被染为红色,不需要花费任何代价。
现在,你可以额外选择一些树边,将它们染成红色,每染一条边需要花费 11 点代价。
请你计算最小的染色代价,使得任意一个被标记的点都可以通过被染成红色的边到达至少一个未被标记的点。并输出不同的染色方案数量。
输入描述:
第一行输入两个整数 n,k(2≦n≦105; 1≦k<n)n,k(2≦n≦105; 1≦k<n) 代表树的节点数、被标记的点数。
第二行输入 kk 个不同的整数 a1,a2,...,ak(1≦ai≦n)a1,a2,...,ak(1≦ai≦n) 代表被标记的点的编号。
此后 n−1n−1 行,第 ii 行输入两个整数 ui,vi(1≦ui,vi≦n,ui≠vi)ui,vi(1≦ui,vi≦n,ui=vi) 代表第 ii 条树边连接 uiui 和 vivi。
输出描述:
在一行上输出两个整数,代表最小的染色代价、满足条件的染色方案数量。由于染色方案数量可能很大,请输出对 109+7109+7 取模后的结果。
示例1
输入:
11 6
8 2 4 7 9 6
8 10
8 9
1 8
1 11
1 3
11 2
5 6
2 5
4 2
7 6
复制输出:
3 4
复制说明:
在这个样例中,树的形态如下图所示。

TOP
ACM 模式
是否格式化代码?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
时间限制:C/C++ 1秒,其他语言
cpp
#include <iostream>
#include <vector>
using namespace std;
typedef long long LL;
const int MOD = 1e9 + 7;
const int maxn = 200005;
bool vis[maxn] ={false};
bool masked[maxn] = {false};
vector<int> G[maxn]; // 邻接表
// 统计v能到达的neighbor数量
void DFS(int v, int &cnt){
if(vis[v]) return;
vis[v] = true;
for(int i=0; i<G[v].size(); i++){
if(masked[G[v][i]] && vis[G[v][i]] == false){ // 若是标记节点继续深入搜索
DFS(G[v][i], cnt);
}else if(vis[G[v][i]] == false){ // 若是没有访问的邻居节点
cnt++;
}
}
}
int main() {
int n, k, u, v;
cin >> n >> k;
for(int i=0; i<k; i++){
cin >> v;
masked[v] = true;
}
for(int i=0; i<n-1; i++){
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
LL ans = 1, cost = 0;
for(int i=1; i<=n; i++){
if(masked[i] && vis[i] == false){
int cnt = 0;
DFS(i, cnt);
cost++; // 最小代价为标记节点连通块数量
ans = (ans * cnt) % MOD; // 方案数为当前方案数乘上每个连通块的邻居节点数量
}
}
cout << cost << ' ' << ans;
return 0;
}
// 64 位输出请用 printf("%lld")