首页 > 其他分享 >CF1917F Construct Tree 题解

CF1917F Construct Tree 题解

时间:2023-12-29 11:25:46浏览次数:29  
标签:le limits int 题解 sum Tree Construct set s1

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

题意

有 \(n\) 条长度 \(l_i\) 的边,问它们是否能组成一棵 \(n + 1\) 个节点的树,使得树的直径长度为 \(d\)。\(n, d \le 2000\)。

题解

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

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

  • 存在一种划分 \(D = D_1 \cup D_2\),使 \(\forall i \notin D, l_i \le \min(s_1 := \sum\limits_{i \in D_1} l_i, s_2:= \sum\limits_{i \in D_2} l_i)\)。

考虑如何计算。将 \(l_i\) 从小到大排序,枚举 \(\max\limits_{i \notin D} i\),则应该满足如下条件:

  • \(\forall j > i, j \in D\);

  • \(\exists x \ge l_i, x = \min(s_1, s_2)\)。

对右侧预处理背包,计算 \(Suf = \set{\sum\limits_{j > i, j \in D_1} l_i}\);对左侧使用双背包,计算 \(DP = \set{(\sum\limits_{j \le i, j \in D_1} l_i, \sum\limits_{j \le i, j \in D_2} l_i)}\)。枚举 \(i\),记 $s = \sum\limits_{j > i} l_i $,则左侧还需要取总长度为 \(d-s\) 的边。提取出 \(Pre = \set{t| (t, d-s-t) \in DP}\),可行的 \(s_1\) 就是位向量 \(Pre\) 与 \(Suf\) 的卷积。对 \(\forall l_i \le x \le \dfrac d 2\),判断 \(s_1\) 是否在卷积中即可。

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

代码实现

#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";
}

标签:le,limits,int,题解,sum,Tree,Construct,set,s1
From: https://www.cnblogs.com/cpchenpi/p/17934117.html

相关文章

  • 在不使用内置函数和中间变量的情况交换数字LeetCode力扣题解面试题16.01
    #异或法#Kotlin```KotlinclassSolution{  funswapNumbers(numbers:IntArray):IntArray{    numbers[0]=numbers[0]xornumbers[1]    numbers[1]=numbers[1]xornumbers[0]    numbers[0]=numbers[0]xor......
  • AT_joisc2015_h 题解
    传送门题意:给定长为\(n\)的字符串\(s\),你可以选择一个区间,将区间内的字符从小到大排序,求可以得到的最长回文子串长度,字符集大小为\(n\)。很有意思的题目。首先容易做到\(O(n^3)\)。考虑怎么优化。我们先考察排序的区间和回文区间的关系。如果两个区间无交,那么显然排序......
  • Layui treeTable 使用【数据不显示、子级不显示】
    //返回JSON数据类publicclassLeaveMessageTreeTable{publicLeaveMessageTreeTable(){this.children=newList<LeaveMessageTreeTable>();this.isParent=false;}publicintId{get;set;}publicintUserId{get;s......
  • CF1835C Twin Clusters 题解
    题目链接点击打开链接题目解法很牛逼的构造题好像随也可以过长度为\(2^{k+1}\)的序列的不同区间有\(2^{2k+1}\)个,值域是\(4^k\),所以一定有解将\(a\)做一遍前缀和,那么问题转化成了找\(s_{r1}\opluss_{l1-1}=s_{r2}\opluss_{l2-1}\)先讲一讲具体做法:按照前\(k\)......
  • ICPC2021Kunming G Glass Bead Game 题解
    QuestionICPC2021KunmingGGlassBeadGame有\(n\)个玻璃珠,\(B_1,B_2,\cdots,B_n\)每一步你可以选择一个\(B_i\)移道第一个位置上,花费的代价为操作前\(B_i\)前面的玻璃珠的个数。已知每一步选择玻璃珠\(B_i\)的概率\(p_i\),问当\(m\rightarrow\infty\)时,在第\(......
  • ICPC2021Kunming G Find the Maximum 题解
    QuestionFindtheMaximum给出一个树,每个点有一个权值\(b_n\),求一条树上路径\(V\),要求\(\frac{\sum_{u\inV(-x^2+b_ux)}}{|V|}\)最大,其中\(x\)是自己选择的一个树Solution先转化一下\(\frac{\sum_{u\inV(-x^2+b_ux)}}{|V|}\),得到\[\frac{\sum_{u\inV(-x^2+b_......
  • CF342E Xenia and Tree
    题意给定一棵\(n\)个节点的一棵树,初始时\(1\)号点为红色,其余为蓝色。要求支持以下操作:将一个节点变为红色。询问节点\(u\)到最近红色节点的距离共\(q\)次操作。Sol喵喵题。不难想到点分树做法,不再阐述。考虑简单的操作分块。对于块外,可以考虑每做完一个块就......
  • vue前端node内存溢出问题解决
    前端项目运行时,如果经常运行慢,崩溃停止服务,报如下错误:FATALERROR:CALL_AND_RETRY_LASTAllocationfailed-JavaScriptheapoutofmemory(JavaScript堆内存不足) 原因:因为在Node中,通过JavaScript使用内存时只能使用部分内存(64位系统:1.4GB,32位系统:0.7GB),这个时候,如......
  • layui 树组件tree 通过API获取数据
    一、简单vartreedata=[]; tree.render({ elem:'#addLeftType', id:'demoId', data:treedata, showCheckbox:true, oncheck:function(obj){ console.log(obj.data);//得到当前点击的节点数据 console.log(obj.checked);//节点是否被选中 console.l......
  • 【CF1917F】Construct Tree
    题目题目链接:https://codeforces.com/contest/1917/problem/F给出\(n\)条边的边权,询问是否可以构造出一棵树,使得所有边都被用上恰好一次且直径为\(d\)。\(n,d\leq2000\)。思路首先肯定是找出一条长度为\(d\)的链,然后判断可不可以把剩下的所有边都挂在这条链的带权重心......