2017多校五 1008题 hdu 6092 Rikka with Subset 背包

34 篇文章 0 订阅
8 篇文章 0 订阅

题目链接


题意:

原有集合 A,基数为 n,其有 2 ^ n 个子集,对每个子集分别求和,对每个和 S,B[S]++.

现给出 B,要求集合 A(按字典序排列)


思路:

也可以考虑当前已经找到的数字,按照生成 B 数组的方式,再开一个 f,把已经找到的数字产生的影响往 f 里面扔。

从小到大去找,比较 b 与 f 的差别,如果 f[p] < b[p],说明当前的 p 是一个新的元素。

p 不可能是 已有的一些元素 与 某个新的元素 的和,如果是这样,那么新的元素必然已经在前面出现并且找到过。


官方题解:

签到题,大致的思想就是反过来的背包。

如果 B_iBi 是 BB 数组中除了 B_0B0 以外第一个值不为 00 的位置,那么显然 ii 就是 AA 中的最小数。

现在需要求出删掉 ii 后的 BB 数组,过程大概是反向的背包,即从小到大让 B_j-=B_{j-i}Bj=Bji

时间复杂度 O(nm)O(nm)


Code:

#include <bits/stdc++.h>
#define maxn 10010
typedef long long LL;
LL b[maxn], ans[55], f[maxn];
void work() {
    int n, m;
    scanf("%d%d", &n, &m);
    memset(f, 0, sizeof(f));
    for (int i = 0; i <= m; ++i) scanf("%lld", &b[i]);
    f[0] = 1;
    int p = 0, tot = 0;
    while (true) {
        if (b[p] == f[p]) ++p;
        else {
            ans[tot++] = p;
            if (tot == n) break;
            for (int i = m; i >= p; --i) f[i] += f[i - p];
        }
    }
    printf("%lld", ans[0]);
    for (int i = 1; i < tot; ++i) printf(" %lld", ans[i]);
    printf("\n");
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}


心塞...出题人说这是签到题Orz

我一开始还十分天真的认为最大的那个肯定是基数为 n 的集合,往下依次找到的 n 个就是基数为 n - 1 的集合,然后还光荣得WA了一发;这种想法就跟认为最小的 n 个数就是 A 集合中的 n 个数一样愚蠢...

最后还是学姐解决了的(躺倒

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值