[ARC084F] XorShift

Anonymely發表於2024-11-06

模擬賽題。

考慮操作的構成,先忽略 \(1\) 操作,只考慮任意兩個數的異或,不難發現所有能構成的數即為線性基。

再考慮 \(1\) 操作,顯然可以對開始的每個數率先進行 \(1\) 操作再構建線性基。

\(lim = \max (\log_2 a, \log_2 m)\),發現所有可能有效的數都不超過 \(2^{2lim}\)

再考慮構建出線性基後如何求答案,從高位到低位考慮,設考慮到第 \(i\) 位,且前面的位都達到了 \(m\) 的上界,轉移分類討論該位置是否選擇即可。

容易發現上面所有操作都能用 bitset 最佳化。

複雜度瓶頸在於構建線性基,樸素實現是 \(O(\frac{lim^3}{w})\) 的,很遺憾是跑不過的,注意這裡會進行 \(lim^2\) 次異或,原因是單個數的 \(1\) 個數最大為 \(lim\),而進行 \(lim\)\(1\) 操作每次需要重新插入。

對於重複插入同一個串多次使用 \(1\) 操作的情況,可以不用每次插入 \(a \times 2^k\),而是將 \(a\) 線上性基中異或完的結果插入,根據線性基的性質正確性得到保證,而 \(1\) 的個數總共為 \(lim\) 個,所以複雜度變為 \(O(\frac{lim^2}{w})\)

考場忘了加取址符,曹操了。

code
const int mod = 998244353;
const int N = 8005;
#define bit bitset <N>
int n, lim;
void read_bit(bit &x) {
	string s;
	cin >> s;
	reverse(all(s));
	for (int i = 0; i < (int)s.length(); i++) x[i] = (s[i] - '0' ? 1 : 0);
}
int getlg(bit &x) {
	if (!x.count()) return -1;
	for (int i = N - 1; ;i--) if (x[i]) return i;
	myassert(0);
}
bit m, a[10];
bit p[N], vis;
void ins(bit &x) {
	for (int i = lim + 1; i >= 0; i--) {
		if (!x[i]) continue;
		if (!vis[i]) {p[i] = x, vis[i] = 1; break;}
		x ^= p[i];
	}
}
int cnt[N], pw[N];

void main01() {
	ios::sync_with_stdio(0), cin.tie(0);
	read(n);
	read_bit(m);
	for (int i = 0; i < n; i++) read_bit(a[i]);
	if (!m.count()) {
		cout << 1 << '\n';
		return ;
	}
	lim = 0;
	for (int i = 0; i < n; i++) ckmax(lim, getlg(a[i]));
	lim += getlg(m);
	for (int i = 0; i < n; i++) {
		for (int c = getlg(a[i]); c <= lim + 1; c++, a[i] <<= 1) ins(a[i]);
	}
	int mxlen = getlg(m);
	for (int i = 0; i <= mxlen; i++) cnt[i] = (i ? cnt[i - 1] : 0) + vis[i];
	pw[0] = 1;
	for (int i = 1; i <= mxlen + 1; i++) pw[i] = 1ll * pw[i - 1] * 2 % mod;
	bit now; now.reset();
	int ans = 0, op = 0;
	for (int i = mxlen; i >= 0; i--) {
		if (m[i]) {
			if (now[i]) {
				if (!vis[i]) continue;
				ans += (i ? pw[cnt[i - 1]] : 1);
				ans %= mod;				
			} else {
				ans += (i ? pw[cnt[i - 1]] : 1);
				ans %= mod;
				if (!vis[i]) {op = 1; break;}
				now ^= p[i];				
			}
		} else {
			if (now[i]) {
				if (!vis[i]) {op = 1; break;}
				now ^= p[i];
			}
		}
	}
	if (!op) (ans += 1) %= mod;
	cout << ans << '\n';
}