欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

CF1917F Construct Tree 题解

Description

给你一个数组 l1,l2,.ln 和一个数字 d。问你是否能够构造一棵树满足以下条件:

  • 这棵树有 n+1 个点。
  • i 条边的长度是 li
  • 树的直径是 d

只需要判断是否有解即可。

2n2000,1d2000,1lid

Solution

先把 l 从大到小排序。

容易发现把直径拉出来后,其他不在直径上的边 lk 挂在直径的点上要满足 lkmin{L,dL},其中 LdL 分别是直径上挂的点左右的长度和。

所以肯定是把不在直径上的边以类似菊花图的形式尽可能挂在中间,使得所有 lkmin{L,dL}

如果 ln>d 显然无解。

如果 ln>d2,则它一定不能是挂着的边,也就是一定在直径上,这时候只要判断 l1,l2.ln1dln 并且能够选出某些数和为 dln

如果 lnd2,并且 ln 在直径上,则其他点一定能全挂上去,所以只要判断能否有若干个和为 dln 即可。如果 ln 不在直径上,那么就需要保证 min{L,dL}ln,所以直接设 fi,j 表示左边和为 i,右边和为 j 是否可行即可求出。

时间复杂度:O(nd2),过不了。

但是注意到 fi,j 的转移只有 0/1,所以可以 bitset 优化至 O(nd2ω)

Code

#include <bits/stdc++.h>
// #define int int64_t
const int kMaxN = 2e3 + 5;
int n, d;
int a[kMaxN];
std::bitset<kMaxN> f[kMaxN];
void dickdreamer() {
std::cin >> n >> d;
for (int i = 1; i <= n; ++i)
std::cin >> a[i];
std::sort(a + 1, a + 1 + n);
if (a[n] > d / 2) {
if (a[n - 1] > d - a[n]) return void(std::cout << "No\n");
f[0].reset();
f[0][0] = 1;
for (int i = 1; i < n; ++i)
f[0] |= (f[0] << a[i]);
std::cout << (f[0][d - a[n]] ? "Yes\n" : "No\n");
} else {
for (int i = 0; i <= d; ++i)
f[i].reset();
f[0][0] = 1;
for (int i = 1; i < n; ++i) {
for (int j = d; ~j; --j) {
f[j] |= (f[j] << a[i]);
if (j >= a[i]) f[j] |= f[j - a[i]];
}
}
bool ans = f[0][d - a[n]];
for (int i = a[n]; i <= d - a[n]; ++i)
ans |= f[i][d - i];
std::cout << (ans ? "Yes\n" : "No\n");
}
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
posted @   下蛋爷  阅读(13)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起