「JSOI2018」潜入行动(树形dp+小优化)

https://loj.ac/problem/2546

直接树形dp设\(f[i][j][0/1][0/1]\)表示\(i\)子树,选了\(j\)个,\(i被覆盖了吗\)\(选了i吗\)

复杂度是\(O(n*k^2)\)

加上子树大小优化,复杂度降为\(O(nk)\),还有其它优化:

考虑度数为1的点,与它唯一相邻的点必须要选。

把必须要选的点找出来,如果\(>k\),则直接输出0。

进行了这个优化后,发现要合并的\(>1\)的子树不超过\(k\)个。

所以复杂度降为\(O(nk+k^3)\)

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int mo = 1e9 + 7;

const int N = 1e5 + 5;

int n, k, x, y, r[N];

vector<int> e[N];
#define pb push_back
#define si size()

int fa[N], siz[N];

int f[N][101][2][2];

ll g[101][2][2];

void dg(int x) {
	siz[x] = 1;
	f[x][0][0][0] = f[x][1][0][1] = 1;
	ff(_y, 0, e[x].si) {
		int y = e[x][_y]; if(y == fa[x]) continue;
		fa[y] = x;
		dg(y);
		fo(i, 0, min(k, siz[x] + siz[y])) fo(j, 0, 1) fo(t, 0, 1) g[i][j][t] = 0;
		fo(i, 0, siz[x]) fo(j, 0, 1) fo(t, 0, 1) if(f[x][i][j][t]) {
			fo(p, 0, min(k - i, siz[y])) fo(q, !t, 1) fo(w, 0, 1) if(f[y][p][q][w]) {
				g[i + p][j | w][t] = (g[i + p][j | w][t] + (ll) f[x][i][j][t] * f[y][p][q][w]) % mo;
			}
		}
		siz[x] = min(k, siz[x] + siz[y]);
		fo(i, 0, siz[x]) fo(j, 0, 1) fo(t, 0, 1)
			f[x][i][j][t] = g[i][j][t] % mo;
	}
}

int bz[N];

int main() {
	scanf("%d %d", &n, &k);
	fo(i, 1, n - 1) {
		scanf("%d %d", &x, &y);
		e[x].pb(y); e[y].pb(x);
		r[x] ++, r[y] ++;
	}
	int cnt = 0;
	fo(i, 1, n) if(r[i] == 1) {
		bz[e[i][0]] = 1;
	}
	fo(i, 1, n) cnt += bz[i];
	if(cnt > k) {
		pp("0\n");
		return 0;
	}
	dg(1);
	ll ans = ((ll) f[1][k][1][0] + f[1][k][1][1]) % mo;
	pp("%lld\n", ans);
}

posted @ 2020-04-21 16:52  Cold_Chair  阅读(179)  评论(0编辑  收藏  举报