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;
}
相關文章
相關標籤/搜索