「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("
")
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
");
		return 0;
	}
	dg(1);
	ll ans = ((ll) f[1][k][1][0] + f[1][k][1][1]) % mo;
	pp("%lld
", ans);
}

原文地址:https://www.cnblogs.com/coldchair/p/12745744.html