题意
给你一棵树,求最小点覆盖集的元素个数和方案个数。
分析
设
f[i][0],f[i][1],f[i][2]
分别为
0:以
i
节点为根节点的子树除了
1:以
i
节点为根节点的子树都被覆盖,且
2:以
i
节点为根节点的子树都被覆盖,且
转移显然,方案数随
f
<script type="math/tex" id="MathJax-Element-2507">f</script>转移即可。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
const LL P = 1e9 + 7;
int n,tot;
int to[N * 2],next[N * 2],g[N];
int f[N][3],F[3];
LL cnt[N][3],nt[3];
void add(int x,int y) {
to[++ tot] = y;
next[tot] = g[x];
g[x] = tot;
}
int calc(int x,int y) {
if (x == 2) return 2;
if (y == 2 || x == 1 && y == 1) return 1;
return 0;
}
void dfs(int u,int fa) {
for (int i = 0;i <= 2;i ++) cnt[u][i] = 1;
f[u][2] = 1,f[u][1] = P,f[u][0] = 0;
for (int i = g[u];i;i = next[i]) {
int v = to[i];
if (v == fa) continue;
dfs(v,u);
for (int j = 0;j <= 2;j ++) F[j] = P,nt[j] = 0;
for (int j = 0;j <= 2;j ++)
for (int k = 0;k <= 2;k ++) {
if (j != 2 && k == 0) continue;
int l = calc(j,k);
F[l] = min(F[l],f[u][j] + f[v][k]);
}
for (int j = 0;j <= 2;j ++)
for (int k = 0;k <= 2;k ++) {
if (j != 2 && k == 0)continue;
int l = calc(j,k);
if (f[u][j] + f[v][k] == F[l])
nt[l] = (nt[l] + cnt[u][j] * cnt[v][k]) % P;
}
memcpy(f[u],F,sizeof(f[u]));
memcpy(cnt[u],nt,sizeof(nt));
}
}
int main() {
scanf("%d",&n);
for (int i = 1;i < n;i ++) {
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
int ans = min(f[1][1],f[1][2]);
int ansn = 0;
for (int i = 1;i <= 2;i ++) if (f[1][i] == ans) ansn = (ansn + cnt[1][i]) % P;
printf("%d\n%d",ans,ansn);
}