[Codeforces]356D - Bags and Coins(构造,背包,bitset优化,输出方案)

比较巧妙的构造题,但是重点实际上在bitset优化和输出方案上。  

题意大概是:

定义节点的分值为子树中叶子节点的个数。现在给定所有节点(只有中间节点,不包含叶子节点)的分值,现在要求构造一棵森林,当用一个虚根把所有森林连起来之后,虚根的权值为s

要求输出方案,每个节点输出他的直接儿子和除开这些儿子它挂了几个叶子。

 

其实我们只需要考虑最上层的一些,最上层的一些加起来刚好等于s,就说明可以组成s,否则的话肯定不行。

然后权值最大的那个节点一定是上层节点,因为它不可能作为任何其他树的子节点。

然后我们发现其实问题就解决了,因为一个树的分值只跟它最上面的节点分值有关,下面不管怎么连都没事,所以我们只需要把剩下的排个序,一个套一个,全部套在最大的那个节点底下。

 

然后重点来了:怎么判断是否存在一系列点加起来刚好等于s

(背包居然是NP完全问题)

一个简单的背包思路fi,j表示前i个物品,能否组成j的体积。

转移就是fi,j=fi1,j||fi1,ja[i]

时间复杂度为O(ns),爆炸了。

 

然后发现这个f数组为01数组,可以用bitset优化,可以除掉32,成功通过。

不过这样仅仅是判断,如何输出方案呢。  

 

以下来自于另一博客的方法。

我们可以分段保存中间结果。

每隔10个,保存以下bitset数组,这样我们就间隔10保存了结果。

倒着推回去,假如fi,j可行,并且用到了a[i],就必存在一个fi1,ja[i]可行,我们从10步之前的那个结果再往后推一下,就能推到fi1的结果了,就能判断是否可行。

有了方案之后还原一下就行了。

 

复制代码
#include <bits/stdc++.h>
#define Mid ((l + r) / 2)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read() {
    char c; int num, f = 1;
    while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    return f * num;
}
const int N = 7e4 + 1;
struct node {
    int id, val, in, nxt;
} a[N];
int n, s, nxt[N];
bitset<N> tmpx[N / 10];
bitset<N> tmp, tmp2;
vector<node> b;
int ok(int x, int y) {
    if(y < 0) return 0;
    tmp2 = tmpx[x / 10];
    for(int i = x / 10 * 10 + 1; i <= x; i++) {
        tmp2 |= (tmp2 << a[i].val);
    }
    return tmp2[y];
}
int cmp(node a, node b) {return a.val < b.val;}
int cmp2(node a, node b) {return a.id < b.id;}
signed main()
{
    n = read(); s = read();
    for(int i = 1; i <= n; i++) a[i].val = read();
    for(int i = 1; i <= n; i++) a[i].id = i;
    sort(a + 1, a + 1 + n, cmp);
    tmp[0] = 1; tmpx[0] = tmp;
    for(int i = 1; i < n; i++) {
        tmp |= (tmp << a[i].val);
        if(i % 10 == 0) tmpx[i / 10] = tmp;
    }
    int tar = s - a[n].val;
    memset(nxt, -1, sizeof(nxt));
    if(ok(n - 1, tar)) {
        int now = s - a[n].val;
        for(int i = n - 1; i; i--) {
            if(now >= a[i].val && ok(i - 1, now - a[i].val)) {
                now -= a[i].val;
                a[i].in = 1;
            }
        }
        int pri = n;
        for(int i = n - 1; i; i--) if(!a[i].in) {
            a[pri].nxt = a[i].id;
            pri = i;
        }
        sort(a + 1, a + 1 + n, cmp2);
        for(int i = 1; i <= n; i++) {
            if(a[i].nxt) {
                printf("%d 1 %d\n", a[i].val - a[a[i].nxt].val, a[i].nxt);
            } else {
                printf("%d 0\n", a[i].val);
            }
        }
        
    } else {
        printf("-1");
    }
    return 0;
}
View Code
复制代码

 

posted @   _onglu  阅读(178)  评论(1编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示