滑蒻稽的博客

【题解】CF1054D Changing Array(异或,贪心)

Des

给你一个序列 \(a\), \(0 \le a_{i}\le2^{k}-1\),你可以修改里面的任意元素任意次,修改方法为把序列里的一个数\(ai⊕\) \(2^{k}-1\) 其中\(⊕\)为异或运算,求最多可以构成多少个连续区间\([l,r]\)使得\(a_{l}⊕a_{l+1}⊕\dots\oplus a_{r-1} ⊕ a_r≠0\)

\(\texttt{Data Range:}\)

\(n\le 2\times 10^5,k\le 30\).

Sol

最大化异或和不等于 0 这个限制太弱,转成最小化等于 0 的个数就会好做一些。

在每一个位置都可以选择异或上 \(2^k-1\),那么对于位置 \(i\)\(a_1\)\(a_i\) 的不同异或和的数量是 \(2^i\) 量级的吗?并不是。由于异或的自反性 \(a\oplus a=0\),所以 \(a_1\)\(a_i\) 的异或和只可能有两种不同的值。

令异或前缀和为 \(s_i\),对于一个右端点 \(r\),会与前面的 \(l\) 组成一个异或和为 0 的区间当且仅当 \(s_{l-1}\oplus s_r=0\)。那么如果在最终的答案中某一种相同的前缀和有 \(m_i\) 种,那么产生的异或和为 0 的区间数量就是 \(\binom{m_i}2\)

我们需要最小化 \(\sum_{i=1}^p\binom{m_i}2\),其中 \(p\) 为不同的前缀和的数量。

要最小化的是 \(\sum_{i=1}^p\frac{m_i^2-m_i}2\),而 \(\sum_{i=1}^pm_i=n\),是一个定值。根据均值不等式,\(m_i\) 应当尽量平均,才能使 \(\sum_{i=1}^p\frac{m_i^2-m_i}2\) 尽量小。

那么对于一个位置 \(i\),设两种前缀和为 \(s_{i,1}\)\(s_{i,2}\). 如果 \(i\) 前面已经有的等于 \(s_{i,1}\) 的前缀和数量比等于 \(s_{i,2}\) 的多,就选择 \(s_{i,1}\) 作为前缀和。反之,选择 \(s_{i,2}\)。这样便能使得 \(m_i\) 尽量平均。

使用 std::map 即可记录某种前缀和的出现次数。

My code

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int n, add, k, S, a[N];
ll cnt;
map<int, int> m;

int main() {
	ios::sync_with_stdio(false); cin.tie(nullptr);
	cin >> n >> k;
	S = (1 << k);
	for(int i = 1; i <= n; i++) cin >> a[i];
	m[0] = 1;
	for(int i = 1; i <= n; i++) {
		int x = add ^ a[i], y = x ^ (S - 1);
		if(m[x] > m[y]) swap(x, y);
		cnt += m[x]++;
		add ^= a[i];
	}
	cout << 1ll * n * (n + 1) / 2 - cnt << '\n';
	
	return 0;
}
posted @ 2021-11-03 12:04  huaruoji  阅读(102)  评论(0编辑  收藏  举报