[Nowcoder 2018ACM多校第八场H] Playing games

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/81701194

题目大意:
原题可以转化成给你一个长度为n的序列 a i , 问最多可以选择多少个数使得异或和为0。 ( n , a i 5 10 5 )

题目思路:
考虑原序列异或和为sum, 转换为求选择最少的数使得异或和sum, 则答案为n减去它。考虑到 a i 的值域, 不超过 2 1 9 , 考虑线性基, 最多选择不超过19个数一定可以构造出sum。 考虑dp, f[i][x]表示使用i个数是否能构造出x, 在有了上界的限制后, 状态数是nlogn级别的,使用fwt转移即可。

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

#define ll long long
#define fi first
#define se second

using namespace std;

const int N = (int)1 << 19;
const int mo = (int)1e9 + 7;
const int inv2 = (int)5e8 + 4;

void FWT_xor(int *a, int n, int opt)
{
    for (int i = 1; i < n; i <<= 1)
        for (int p = i << 1, j = 0; j < N; j += p)
            for (int k = 0; k < i; k ++){
                int X = a[j + k], Y = a[i + j + k];
                a[j + k] = (X + Y) % mo;
                a[i + j + k]= (X + mo - Y)% mo;
                if(opt == -1){
                    a[j + k] = 1ll * a[j + k] * inv2 % mo;
                    a[i + j + k] = 1ll * a[i + j + k] * inv2 % mo;
                }
            }
}

int n, sum, a[N], b[30][N];

bool check(int x){
    FWT_xor(b[x], N, -1);
    return b[x][sum];
}


int main()
{

    scanf("%d", &n);
    for (int i = 1, x; i <= n; i ++){
        scanf("%d", &x);
        sum ^= x;
        a[x] = 1;
    }

    if (sum == 0) {printf("%d\n", n); return 0;}

    //a[0] = 1;

    //for (int i = 0; i < N; i ++) b[0][i] = 1;

    b[0][0] = 1;
    FWT_xor(a, N, 1);
    FWT_xor(b[0], N, 1);
    for (int i = 1; i <= 19; i ++)
        for (int j = 0; j < N; j ++) b[i][j] = 1ll * a[j] * b[i - 1][j] % mo;

    int l = 1, r = 19; int ans = -1;
    while (l <= r){
        int mid = (l + r) / 2;
        if (check(mid)) r = mid - 1, ans = mid;
        else l = mid + 1;
    }

    printf("%d\n", n - ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013578420/article/details/81701194