码迷,mamicode.com
首页 > 其他好文 > 详细

cf1058E 思维 前缀处理

时间:2018-10-05 16:13:54      阅读:212      评论:0      收藏:0      [点我收藏+]

标签:col   size   ali   cal   分享图片   efi   std   div   padding   

题目大意:给定一个长度为 (n≤3×105)的数列ai(1ai1018)交换一个数的任意二进制位,问你可以选出多少 区间经过操作后异或和是 0 

链接:http://codeforces.com/contest/1058/problem/E

 

思路:由于二进制随意交换,那么它本身值不必考虑,只需要保存它有多少二进制为1的个数就好了。

充分必要条件:

  • 区间中二进制1的个数是偶数
  • 区间中二进制位最多的一个数的二进制个数小于等于和的一半

对于条件一 技术分享图片

所以前面的为偶数的前提下  , 根据(0,l)的奇偶性 来判断 (l,r)的奇偶性  所以 ,将所有前缀和的奇偶性统计出来  O(n)的负责度

暂且将条件一全加上  然后再在条件二中减去

因为ai <1e18 >1 的限制所以枚举大约60位 就可以截至  

 

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define pb push_back
#define fi first
#define se second
#define all(v) v.begin(),v.end()
#define forn(i,a,n) for(int i=a;i<n;++i)


const int N = 3e5+4;
ll a[N],b[N];

ll cbit(ll x){
    ll res=1;
    while(x){
        if(x&1)res++;
        x/=2;
    }
    return res-1;
}
ll sum[N];
int cnt[2];

int main(){

    int n;
    cin>>n;
    forn(i,1,n+1){
        scanf("%lld",a+i);
        b[i]= cbit(a[i]);
    }
    ll ans=0 ;

      cnt[0]=1;

    //1 (1..j-1) , j  为偶数   // 这里用公式说明一下
    forn(i,1,n+1){
        sum[i] = sum[i-1]+b[i];
        ans += cnt[sum[i]&1]; //1 (0..j) , j  为偶数   // 这里用公式说明一下

        //根据条件2 倒着减过去
        int j =i,k=i;
        ll  Max = b[i];

        while(k>=1 && j-k<=62 ){
            Max = max(Max,b[k]);
           if(  Max*2 > sum[i]-sum[k-1] && ( (sum[i]-sum[k-1] )%2==0 )  )ans--;
           k--;
        }
        cnt[sum[i]&1]++;
        //cout<<ans<<endl;

    }

    cout<<ans<<endl;

    return 0;
}

 

---恢复内容结束---

 

cf1058E 思维 前缀处理

标签:col   size   ali   cal   分享图片   efi   std   div   padding   

原文地址:https://www.cnblogs.com/wjhstudy/p/9744816.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!