[BZOJ2844]albus就是要第一个出场(线性基)

Address

Solution

  • 本题只不过是考查对线性基的理解
  • 先介绍下一个显然的结论
  • 如果 n n 个数构成的异或线性基有 m m 个数,并且通过异或运算能使得数 n u m num 被表出
  • 那么这 n n 个数有 2 n m 2^{n-m} 个子集的异或和为 n u m num
  • 感性的理解: n n 个数在构建线性基的过程中,有 n m n-m 个数没有被扔进线性基
  • 这样这 n n 个数可以等效地看作为线性基内的 m m 个数以及 n m n-m 0 0
  • 于是这个结论就比较显然了(每个 0 0 都可以任意选或不选)
  • 回到原问题
  • 发现我们要求的就是有多少个子集的异或和不超过 q 1 q-1 ,最后加上 1 1 即为答案
  • 考虑按位统计,把 q q 拆成不超过 O ( log q ) O(\log q) 2 x 2^x 形式的数之和
  • 转化成不超过 O ( log q ) O(\log q) 个形如下面的问题
  • 有多少个子集的异或和在 [ 2 k × a , 2 k × ( a + 1 ) 1 ] [2^k\times a,2^k\times(a+1)-1] 范围内
  • 也就是有多少个子集的异或和去掉最低 k k 个二进制位之后为 a a
  • 将每个数除以 2 k 2^k 后构建线性基即可计算出
  • 复杂度 O ( n log A log q ) O(n\log A\log q)

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

const int N = 1e5 + 5, M = 40, ZZQ = 10086;

int n, a[N], q, bas[M], m, ans = 1;

int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = 1ll * res * a % ZZQ;
		a = 1ll * a * a % ZZQ;
		b >>= 1;
	}
	return res;
}

void ins(int x)
{
	int i;
	Rof (i, 30, 0)
	{
		if (!((x >> i) & 1)) continue;
		if (bas[i] == -1) return (void) (bas[i] = x);
		else x ^= bas[i];
	}
	m++;
}

bool canbe(int x)
{
	int i, res = 0;
	Rof (i, 30, 0)
	{
		if (bas[i] == -1) continue;
		if (((res >> i) & 1) ^ ((x >> i) & 1)) res ^= bas[i];
	}
	return res == x;
}

int query(int offset, int val)
{
	int i;
	m = 0;
	memset(bas, -1, sizeof(bas));
	For (i, 1, n) ins(a[i] >> offset);
	if (canbe(val)) return qpow(2, m);
	return 0;
}

int main()
{
	int i;
	n = read();
	For (i, 1, n) a[i] = read();
	q = read();
	if (!q) return (void) puts("1"), 0;
	q--;
	Rof (i, 30, 0)
		if ((q >> i) & 1)
			ans = (ans + query(i, (q >> i) ^ 1)) % ZZQ;
	memset(bas, -1, sizeof(bas)); m = 0;
	For (i, 1, n) ins(a[i]);
	if (canbe(q)) ans = (ans + qpow(2, m)) % ZZQ;
	std::cout << ans << std::endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/84838295