您的位置:首页 > 其它

轮舞前夕

2015-08-12 20:17 246 查看

题意

给你一棵树,求最小点覆盖集的元素个数和方案个数。

分析

设f[i][0],f[i][1],f[i][2]f[i][0],f[i][1],f[i][2]分别为

0:以ii节点为根节点的子树除了ii节点都被覆盖的最小值;

1:以ii节点为根节点的子树都被覆盖,且ii节点不在集合中的最小值。

2:以ii节点为根节点的子树都被覆盖,且ii节点在集合中的最小值。

转移显然,方案数随ff转移即可。

#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
;
int f
[3],F[3];
LL cnt
[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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: