1. 程式人生 > >[BZOJ2844]albus就是要第一個出場(線性基)

[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;
}