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


    Des

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

    ( exttt{Data Range:})

    (nle 2 imes 10^5,kle 30).

    Sol

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

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

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

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

    要最小化的是 (sum_{i=1}^pfrac{m_i^2-m_i}2),而 (sum_{i=1}^pm_i=n),是一个定值。根据均值不等式,(m_i) 应当尽量平均,才能使 (sum_{i=1}^pfrac{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 << '
    ';
    	
    	return 0;
    }
    
  • 相关阅读:
    栈区,堆区,全局区,文字常量区,程序代码区 详解
    2010年IT行业十大收购
    三大数据备份方式:完全备份、增量备份以及差异备
    Driver Development Part 1: Introduction to Drivers (code project)
    手工构造典型PE文件(转)
    访问IIS元数据库失败[转]
    代码注入的三种方法(转)
    对象的初始化(转)
    网络和黑客编程基本知识 (转)
    破解linux中root密码(图) 转自csdn
  • 原文地址:https://www.cnblogs.com/huaruoji/p/CF1054D.html
Copyright © 2020-2023  润新知