Codeforces Round #512 (Div. 2, based on Technocup 2019 Elimination Round 1) E. Vasya and Good Sequen

题目大意

给出 n n n个数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an,每个数在二进制下,可以任意交换0和1的位置(也可以不交换),问有多少个区间 [ l , r ] [l,r] [l,r]满足在每个数在操作之后的异或和为0。

时间限制

2s

数据范围

n ≤ 3 × 1 0 5 n\le 3\times 10^5 n3×105
a i ≤ 1 0 18 a_i\le 10^{18} ai1018

题解

不难发现,只需要关心这个数在二进制下1的个数,而不要具体关注这个数是什么。
那么考虑一下满足条件的区间中1的个数,
显然1的个数一定是偶数,不然1无法两两匹配在异或的时候消去。
除了这个条件,还有没有别的条件,
显然是有的,来看这种情况:
两个相邻的数,一个数有31个1,另一个数只有1个1,它们的和是偶数,但是它们并不满足条件。

因此多了一个条件,这个区间其它数1的个数的和要比的最多1的那个数要大。

不难发现,这个最大值只有60,对于这部分情况可以之间枚举,其余的情况通过前缀和计算。

Code

//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#define ll long long
#define G getchar
using namespace std;

ll read()
{
    char ch;
    for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
    ll n = 0 , w;
    if (ch == '-')
    {
        w = -1;
        ch = G();
    } else w = 1;
    for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
    return n * w;
}

void write(int x)
{
    if (x > 9)
    {
        write(x / 10);
        putchar(x % 10 + 48);
    }
    else putchar(x + 48);
}

const int N = 300005;

int n , s[N] , num[N][3] , tmp , mx , a[N];
ll ans , x , z[63];

int main()
{
    //freopen("d.in","r",stdin);
    //freopen("e.out","w",stdout);
    
    z[0] = 1;
    for (int i = 1 ; i < 63 ; i++)
        z[i] = z[i - 1] << 1;

    n = read();
    num[0][0] = 1;
    memset(a , 0 , sizeof(a));
    for (int i = 1 ; i <= n ; i++)
    {
        x = read();
        for (int j = 0 ; z[j] <= x ; j++)
            if (z[j] & x) a[i]++;

        s[i] = (s[i - 1] + a[i]) % 2;
        num[i][0] = num[i - 1][0];
        num[i][1] = num[i - 1][1];
        num[i][s[i]]++;
        mx = tmp = a[i];
        for (int j = i - 1 ; j ; j--)
        {
            if (tmp > 120)
            {
                ans = ans + num[j - 1][s[i]];
                break;
            }
            tmp = tmp + a[j];
            mx = max(mx , a[j]);
            if (tmp >= mx * 2 && tmp % 2 == 0) ans++;
        }
    }
    printf("%lld\n", ans);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值