CF1917F Construct Tree 题解

题目链接:https://codeforces.com/contest/1917/problem/F

题意

n 条长度 li 的边,问它们是否能组成一棵 n+1 个节点的树,使得树的直径长度为 dn,d2000

题解

首先当然要存在一个边集 D,使得 iDli=d,这可以使用背包判断。但这个条件不充分,例如第二个样例 ({1,4,3,4},7),虽然存在 {2,3} 使 l2+l3=7,但直径的长度不小于 8

考虑对于一个给定的 D 如何构造这棵树。使用调整法等可证,最优的一个方案是找到直径的中点,把其它所有边都挂在中点上。因此,其余所有边的长度不能大于直径较短一侧的链长度,D 可以成为直径的充要条件是:

  • 存在一种划分 D=D1D2,使 iD,limin(s1:=iD1li,s2:=iD2li)

考虑如何计算。将 li 从小到大排序,枚举 maxiDi,则应该满足如下条件:

  • j>i,jD

  • xli,x=min(s1,s2)

对右侧预处理背包,计算 Suf={j>i,jD1li};对左侧使用双背包,计算 DP={(ji,jD1li,ji,jD2li)}。枚举 i,记 s=j>ili,则左侧还需要取总长度为 ds 的边。提取出 Pre={t|(t,dst)DP},可行的 s1 就是位向量 PreSuf 的卷积。对 lixd2,判断 s1 是否在卷积中即可。

时间复杂度的瓶颈在于双背包的转移和位向量卷积计算。使用 bitset 加速计算,则它们都可以在 O(d2w) 内完成,总时间复杂度为 O(nd2w)

代码实现

#include <bits/stdc++.h>
using namespace std;
using i64 = int64_t;
using bs = bitset<2023>;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, d;
cin >> n >> d;
vector<int> l(n);
for (int &x : l) cin >> x;
ranges::sort(l);
int s = accumulate(l.begin(), l.end(), 0);
if (s == d) return void(cout << "Yes\n");
vector suf(n + 1, bs(1));
for (int i = n - 1; i >= 0; i--) {
suf[i] = suf[i + 1] | (suf[i + 1] << l[i]);
}
bool ans = false;
vector dp(d + 1, bs(0));
dp[0][0] = 1;
for (int i = 0; i < n; i++) {
int x = l[i];
s -= x;
for (int s1 = d; s1 >= 0; s1--) {
dp[s1] |= dp[s1] << x;
if (s1 >= x) { dp[s1] |= dp[s1 - x]; }
}
bs vis{0}, pre{0};
for (int j = 0; j <= d - s; j++) pre[j] = dp[j][d - s - j];
// convolution (suf[i+1], pre)
for (int j = 0; j <= s && j <= d / 2; j++)
vis[j] = (pre & (suf[i + 1] >> (s - j))).any();
for (int j = s; j <= d / 2; j++)
vis[j] = (pre & (suf[i + 1] << (j - s))).any();
for (int s1 = x; s1 <= d / 2; s1++) { ans |= vis[s1]; }
}
cout << (ans ? "Yes" : "No") << "\n";
}
posted @   cccpchenpi  阅读(37)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示