Spices(线性基、贪心)

题意

\(2^n-1\)个数字,分别是\(1,2,\dots,2^n-1\)。它们具有权值,分别为\(c_1,c_2,\dots,c_{2^n-1}\)

从这些数字中选择一些数字组成集合\(S\),对于\(1,2,\dots,2^n-1\)中任意一个数字\(i\),都可以从\(S\)中找到一个子集,使得子集里面所有数异或起来为\(i\)

问:集合\(S\)中所有数权值之和的最小值为多少?

数据范围

\(2 \leq n \leq 16\)

思路

线性基模板题。

首先不考虑权值的问题,只考虑集合\(S\)如何得到。这个问题为线性基的基本应用,只需要在插入的过程中,记录当前元素是否使用了即可。

现在考虑权值,只需要在上面的基础上,对权值进行排序即可(因为在\(b\)数组的构造过程中,肯定是优先选择权值小的数)。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long ll;
typedef pair<ll, int> pii;

const int N = 20;

int n;
pii a[1 << N];
ll b[N];
ll ans;

void ins(int x, ll val) //插入数字,获取线性基
{
    for(int i = 15; i >= 0; i --) {
        if(x >> i & 1) {
            if(!b[i]) {
                b[i] = x;
                ans += val;
                return;
            }
            else x ^= b[i];
        }
    }
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i < (1 << n); i ++) {
        ll x;
        scanf("%lld", &x);
        a[i] = {x, i};
    }
    sort(a + 1, a + (1 << n));
    for(int i = 1; i < (1 << n); i ++) {
        ins(a[i].second, a[i].first);
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2022-03-16 18:38  pbc的成长之路  阅读(148)  评论(0编辑  收藏  举报