CodeForces 1030E Vasya and Good Sequences 位运算 思惟

原题连接

ios

题意


  • 目前咱们有一个长为n的序列,咱们能够对其中的每个数进行任意的二进制重排(改变其二进制表示结果的排列),问咱们进行若干次操做后获得的序列,最多能有多少对 \(l, r\) 使得 \([l, r]\)区间内的异或和为0。

思路


  • 首先注意到“二进制重排”,实际上也就是说,\(a_i\)是多少不重要,有用的信息是它自己有多少个1。

    spa

  • 而后,\([L, r]\)内异或和为0,能够认为是这个区间内的1可以相互抵消。那么这个区间内1的个数必定是偶数个。

    code

  • 咱们考虑1的个数为偶数个的状况下,如何才会出现不能相互抵消的状况。这种“特殊状况”其实就是存在一个数,它的1的数量比其余的数加起来都多。除了这种状况外,其余状况下均可以所有抵消。

    rem

  • 那么咱们能够预处理出 \(pre[i]\),表明\(1 - i\)中有多少个1,而后从头开始扫描,同时记录两个变量,一是\(1 - i - 1\)中有多少个\(pre[j]\)为奇数,二是多少个为偶数。而后咱们在\(i\)处时,给答案加上表明与\(pre[i]\)相同奇偶性的变量便可。

    get

  • 在扫描的同时,咱们须要减去答案中的全部符合“特殊状况”的\([l, i]\)区间数量。可是咱们并不须要从1开始枚举,由于每一个二进制数最多不超过63个1,因此事实上咱们的枚举范围为\([i - 63, i]\)便可,剩下的长度大于63的区间必定不会符合“特殊状况”。

    string

AC代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>

using namespace std;

const int N = 300000;

long long abss(long long a)
{
	if (a < 0)
	{
		a = -a;
	}
	return a;
}

long long n;
long long xx;
long long aa[N + 5] = {0}, su[N + 5];
long long mm[105] = {0};

int main()
{
	mm[0] = 1;
	scanf("%lld", &n);
	long long ans = 0;
	su[0] = 0;
	for (int i = 1; i <= n; ++i)
	{
		scanf("%lld", &xx);
		while (xx)
		{
			if (xx & 1)
			{
				++aa[i];
			}
			xx >>= 1; 
		}
		su[i] = su[i - 1] + aa[i];
	}
	long long l[2] = {1, 0};
	aa[0] = 0;
	for (int i = 1; i <= n; ++i)
	{
		int bitt = su[i] & 1;
		ans += l[bitt];
		long long mx = aa[i];
		long long rem = 0;
		for (int j = i - 1; j >= 0 && j >= i - 63; --j)
		{
			if ((su[i] - su[j]) % 2 == 0 && rem < mx)
			{
				--ans;
			}
			if (mx < aa[j])
			{
				rem += mx;
				mx = aa[j];
			}
			else
			{
				rem += aa[j];;
			}
		}
		++l[bitt];;
	}
	printf("%lld", ans);;
	return 0;
}
相关文章
相关标签/搜索