CodeForces 1917F Construct Tree

发布时间 2023-12-26 18:05:07作者: zltzlt

洛谷传送门

CF 传送门

考虑形式化地描述这个问题。先把 \(l\) 排序。然后相当于是否存在一个 \(\{1, 2, \ldots, n\}\) 的子集 \(S\),使得:

  • \(\sum\limits_{i \in S} l_i = d\)
  • \(\exists T \subseteq S, \max\limits_{i \notin S} l_i \le \min(\sum\limits_{i \in T} l_i, \sum\limits_{i \in S \land i \notin T} l_i)\)

注意到若 \(n - 1 \in S \land n \in S\) 则第二个条件一定满足,让 \(n - 1 \in T\)\(n \notin T\) 即可。所以如果 \(l_1, l_2, \ldots, l_{n - 2}\) 能凑出来 \(d - a_{n - 1} - a_n\) 就可行。

然后依次讨论 \(\max\limits_{i \notin S} i = n - 1\)\(n\) 的情况。

  • \(\max\limits_{i \notin S} i = n - 1\),那么 \(n \in S\)。若前 \(n - 2\) 个元素能凑出来和为 \(x\)\(d - x - a_n\) 的两个不相交集合,且 \(a_{n - 1} \le \min(x + a_n, d - x - a_n)\) 就可行。
  • \(\max\limits_{i \notin S} i = n\),那么若前 \(n - 1\) 个元素能凑出来和为 \(x\)\(d - x\) 的两个不相交集合,且 \(a_n \le \min(x, d - x)\) 就可行。

二维可行性背包考虑 bitset 优化,复杂度 \(O(\frac{nd^2}{\omega})\)

code
// Problem: F. Construct Tree
// Contest: Codeforces - Codeforces Round 917 (Div. 2)
// URL: https://codeforces.com/contest/1917/problem/F
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 2020;

int n, m, a[maxn];
bitset<maxn> f[maxn], g;

void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
	}
	sort(a + 1, a + n + 1);
	for (int i = 0; i <= m; ++i) {
		f[i].reset();
	}
	g.reset();
	f[0].set(0);
	g.set(0);
	for (int i = 1; i < n; ++i) {
		if (i == n - 1) {
			if (a[n - 1] + a[n] <= m && g.test(m - a[n - 1] - a[n])) {
				puts("Yes");
				return;
			}
			for (int j = 0; j <= m - a[n]; ++j) {
				if (a[i] <= min(j + a[n], m - a[n] - j) && f[j].test(m - j - a[n])) {
					puts("Yes");
					return;
				}
			}
		}
		for (int j = m; ~j; --j) {
			f[j] |= (f[j] << a[i]);
			if (j >= a[i]) {
				f[j] |= f[j - a[i]];
			}
		}
		g |= (g << a[i]);
	}
	for (int i = a[n]; i <= m - a[n]; ++i) {
		if (f[i].test(m - i)) {
			puts("Yes");
			return;
		}
	}
	puts("No");
}

int main() {
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}