版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/83144897
题目链接<http://codeforces.com/contest/1053/problem/B>
题意:
定义一个操作:把数字中的两位进行交换。给出一个序列,问存在多少个子区间,对区间内的数字进行无限次操作后使得区间内数字的异或和为0。计算符合条件的区间数。
题解:
如果一个区间内1的个数和为奇数那一定不可取。
对于偶数的情况,如果1的个数最大的数小于等于其余数字之和,就一定能构造出来。
因为数字的范围是1e18,所以1的个数不超过60。对于区间长度不超过60的可以直接暴力,超过的可以预处理前缀和的奇偶来计算sum是偶数的个数。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=3e5+7;
ll n,a[N],sum[N],cnt[N][2];
ll x;
ll get(ll x){
ll res=0;
while(x){
if(x&1) res++;
x>>=1;
}
return res;
}
int main()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
scanf("%lld",&x);
a[i]=get(x);
sum[i]=sum[i-1]+a[i];
}
for(ll i=n;i>=1;i--){
cnt[i][0]=cnt[i+1][0];
cnt[i][1]=cnt[i+1][1];
if(sum[i]%2) cnt[i][1]++;
else cnt[i][0]++;
}
ll ans=0;
for(ll i=1;i<=n;i++){
ll len=min(i+60,n);
ll maxn=a[i];
for(ll j=i+1;j<=len;j++){
maxn=max(maxn,a[j]);
if((sum[j]-sum[i-1]-maxn>=maxn)&&((sum[j]-sum[i-1])%2==0)) ans++;
}
ans+=cnt[len+1][sum[i-1]%2];
}
printf("%lld\n",ans);
}