版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/81701194
题目大意:
原题可以转化成给你一个长度为n的序列
, 问最多可以选择多少个数使得异或和为0。
题目思路:
考虑原序列异或和为sum, 转换为求选择最少的数使得异或和sum, 则答案为n减去它。考虑到
的值域, 不超过
, 考虑线性基, 最多选择不超过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;
}