题目大意:给定一个长度为 (n≤3×105)的数列ai(1≤ai≤1018)交换一个数的任意二进制位,问你可以选出多少 区间经过操作后异或和是 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; }
---恢复内容结束---
原文地址:https://www.cnblogs.com/wjhstudy/p/9744816.html
时间: 2024-10-21 00:27:35