小清新树形 DP


HNOI/AHOI2018 道路

暴力 DP + 优化空间。

记录

ZJOI2007 时态同步

记录

NOI2002 贪吃的九头龙

首先判断无解。

然后对于小头数量 \(\ge2\) 的情况,只有大头需要算树枝,这是因为两种颜色就可以对树进行独立染色。

于是设 \(f(u,s,1/0)\) 表示子树 u, 大头吃了 s 个果, u 是否被大头吃的。

转移时合并子树,方程为:

\[f(u,s,k) = \min_{0\le t\le s}\Bigg[f(u,t,k) + \min\{f(v,s-t,k\oplus1),f(v,s-t,k)+[k=1\mid m=2]W(u,v)\}\Bigg] \]

记录

POI2013 LUK-Triumphal arch

首先可以二分 k, 接下来解决判定性问题。

假设 1 为根。

可以发现,B 一定不会向 1 走。

\(f(u)\) 表示点 \(u\) 需要多染多少次才能让 \(A\) 胜利。

记录

SP3734 PERIODNI - Periodni

算法上,笛卡尔树秒天秒地;

实现上,原地卷积恐怖如斯

(bushi

SPOJ 上不去, luogu remote judge 上不去,vjudge 也崩,就贴个代码吧。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;

const int mo = 1e9 + 7, N = 503;

int ksm (int a, int b) {
	int res = 1;
	for (; b; b >>= 1, a = (LL)a * a % mo)
		if (b & 1) res = (LL)res * a % mo;
	return res;
}

int n, k, h[N];
int sta[N], tp, ls[N], rs[N];
int fac[1000003], ifac[1000003];

int C (int x, int y) {
	if (y > x) return 0;
	return (LL)fac[x] * ifac[y] % mo * ifac[x - y] % mo;
}

int f[N][N], siz[N];
void dfs (int x, int delt) {
	f[x][0] = 1;
	siz[x] = 1;
	int Hight = h[x] - delt;
	if (ls[x])
	{
		int y = ls[x];
		dfs (y, h[x]);
		for (int i = min (k, siz[x] + siz[y]); i >= 0; -- i)
			for (int j = max (1, i - siz[x]); j <= siz[y] && j <= i; ++ j)
				f[x][i] = (f[x][i] + (LL)f[y][j] * f[x][i - j] % mo) % mo;
		siz[x] += siz[y];
	}
	if (rs[x])
	{
		int y = rs[x];
		dfs (y, h[x]);
		for (int i = min (k, siz[x] + siz[y]); i >= 0; -- i)
			for (int j = max (1, i - siz[x]); j <= siz[y] && j <= i; ++ j)
				f[x][i] = (f[x][i] + (LL)f[y][j] * f[x][i - j] % mo) % mo;
		siz[x] += siz[y];
	}
	for (int i = min (k, siz[x]); i >= 0; -- i)
		for (int j = 0; j < i && j < siz[x]; ++ j)
			f[x][i] = (f[x][i] + (LL)f[x][j] * C (Hight, i - j) % mo * C (siz[x] - j, i - j) % mo) % mo;
}

int main()
{
	fac[0] = 1;
	for (int i = 1; i <= 1000000; ++ i)
		fac[i] = (LL)i * fac[i - 1] % mo;
	ifac[1000000] = ksm (fac[1000000], mo - 2);
	for (int i = 1000000; i >= 1; -- i)
		ifac[i - 1] = (LL)i * ifac[i] % mo;
	
	scanf ("%d%d", & n, & k);
	for (int i = 1; i <= n; ++ i) {
		scanf ("%d", & h[i]);
		while (tp && h[sta[tp]] > h[i]) ls[i] = sta[tp --];
		if (tp) rs[sta[tp]] = i;
		sta[++ tp] = i;
	}
	int root = sta[1];
	dfs (root, 0);
	cout << f[root][k];
	return 0;
}
posted @ 2021-03-16 22:24  xwmwr  阅读(72)  评论(0编辑  收藏  举报