BZOJ5314 JSOI2018 潜入行动
BZOJ5314 JSOI2018 潜入行动
题目描述
给你一棵树,选取k个点放一个监听器,一个监听器只能够覆盖与它相邻的点,要求所有的点都被覆盖。求方案数.
解题思路
首先要往树形dp方面去思考。
然后就是丧心病狂的分类讨论
设\(f[u][i][0/1][0/1]\) 表示\(u\)子树内选了\(i\)个点,节点\(u\)是否被覆盖,是否装了一个监听器。(不要问我怎么设出来的,一开始搞了3个0/1状态的,发现可以少一个,瞎jb多试试看那个状态好分类,好转移)
这样可以把子树\(u\)分成4种,然后枚举两种合并讨论。
然而有一些情况是不能够合并的,会出现重复的情况。
比如\(00+00\) 得到的新的树不是一个合法的树,这时必须在u放一个监听器,然而这种状态会在\(01+00\)中被算到一次。要避免这种。
所以我们讨论了16种情况之后发现,如果这次要添加一个监听器,那肯定是不对的。我们要一开始就确定好这个点是否放监听器,后面就可以不用再管了,不然会重复。 稍微画画图,分个类。
关于复杂度,乍一看是\(O(nk^2)\),但是有人证明了,是\(O(nk)\)的。请务必记住结论(这个结论怎么证的反正不会考)。小心后面再次遇见这种题目。(比如昨天的ZROI把我搞自闭了)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 11;
const int mod = 1e9 + 7;
int n, K, sz[N];
int f[N][111][2][2], G[111][2][2], F[111][2][2], tmp[111][2][2];
int head[N], to[N<<1], nex[N<<1], size;
void add(int x, int y){
to[++size] = y;
nex[size] = head[x];
head[x] = size;
}
void dfs(int u, int fa){
f[u][0][0][0] = f[u][1][0][1] = 1;
sz[u] = 1;
for(int i = head[u];i;i = nex[i]){
int v = to[i];
if(v == fa)continue;
dfs(v, u);
memcpy(F, f[u], sizeof F);
memcpy(G, f[v], sizeof F);
for(int j = 0;j <= sz[u] && j <= K; j++){
for(int k = 0;k <= sz[v] && j + k <= K; k++){
for(int a = 0;a <= 1; a++){
for(int b = 0;b <= 1; b++){
(tmp[j+k][b][1] += 1LL * F[j][0][1] * G[k][a][b] % mod) %= mod;
(tmp[j+k][1][1] += 1LL * F[j][1][1] * G[k][a][b] % mod) %= mod;
}
}
(tmp[j+k][1][0] += (1LL * F[j][1][0] * (G[k][1][1] + G[k][1][0]) % mod)) %= mod;
(tmp[j+k][0][0] += 1LL * F[j][0][0] * G[k][1][0] % mod) %= mod;
(tmp[j+k][1][0] += 1LL * F[j][0][0] * G[k][1][1] % mod) %= mod;
}
}
sz[u] += sz[v];
for(int j = 0;j <= K; j++){
for(int a = 0;a <= 1; a++){
for(int b = 0;b <= 1; b++){
f[u][j][a][b] = tmp[j][a][b];
tmp[j][a][b] = 0;
}
}
}
}
/*printf("u=%d\n", u);
for(int i = 0;i <= K; i++){
printf("i=%d %d %d %d %d\n", i, f[u][i][0][0], f[u][i][0][1], f[u][i][1][0], f[u][i][1][1]);
}*/
}
int main(){
freopen("5314.in", "r", stdin);
freopen("5314.out", "w", stdout);
cin>>n>>K;
int u, v;
for(int i = 1;i < n; i++){
scanf("%d%d", &u, &v);
add(u, v); add(v, u);
}
dfs(1, 1);
printf("%d\n", (f[1][K][1][0] + f[1][K][1][1]) % mod);
return 0;
}