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

来源:互联网 发布:手机project软件 编辑:程序博客网 时间:2024/06/06 09:22

题目链接


题意:

原有集合 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 10010typedef 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 个数一样愚蠢...

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

原创粉丝点击