题目:http://codeforces.com/contest/1058/problem/E
粘不过来题目,自己点开看吧QAQ
题目大意:给你一列数,现在有一种操作,就是对一个数,可以任意交换其二进制的两位,操作次数不限。问你有多少个字串 [ l , r ],可以经过上述操作,使该字串的异或和为0。
题解:
一段数的异或和为0,即让这一串数二进制下每一位的1的个数都为0。于是题目可以简化成:先搞出来每个数的二进制下1的个数,比如:3 4 6 7,然后每次可以挑出两个数,让它们都减1(即消去一位)。然后看能不能把所有数都变成0。
进一步观察,我们发现只要最大的数小于等于剩余的数之和即可消完。即对于一段数 max(l,r) * 2 <= sum(l,r) 且 sum(l,r)为偶数即可。
对于这个问题,我们发现任意的数的二进制下1的个数都大于0,且小于等于64。也就是 max(l,r) 小于等于64,max(l,r)*2 <=128 ,那么对于长度大于128的区间,一定满足 max(l,r) * 2 <= sum(l,r) ,我们只需要找有多少个区间满足 sum(l,r)为偶数。
对于长度小于等于128的区间,暴力判断即可。
RMQ打错真是没谁了QAQ
代码:
#include<bits/stdc++.h>
#define LL long long
#define MAXN 300050
using namespace std;
int n,b[MAXN],cf[25],Log2[MAXN],MX[MAXN][25],s[MAXN],js[MAXN],os[MAXN];
LL a[MAXN];
void RMQ()
{
int i,j;
for(i=1;i<=n;i++)MX[i][0]=b[i];
for(j=1;cf[j]<=n;j++)
{
for(i=1;i+cf[j]-1<=n;i++)
{
MX[i][j]=max(MX[i][j-1],MX[i+cf[j-1]][j-1]);
}
}
/*for(i=1;i<=n;i++)
{
for(j=1;i+cf[j]-1<=n;j++)
{
MX[i][j]=max(MX[i][j-1],MX[i+cf[j-1]][j-1]);
}
}*/
}
int Mx(int ql,int qr)
{
int i=Log2[qr-ql+1];
return max(MX[ql][i],MX[qr-cf[i]+1][i]);
}
int main()
{
int l1,i,len,l,r,mx1;
LL aa,ans;
scanf("%d",&n);
cf[0]=1;for(i=1;i<=20;i++)cf[i]=cf[i-1]*2;
for(i=1;i<=300000;i++)Log2[i]=log2(i);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
for(i=1;i<=n;i++)
{
aa=a[i];
while(aa>0LL)
{
b[i]+=(int)(aa%2LL);
aa/=2LL;
}
}
s[0]=0;for(i=1;i<=n;i++)s[i]=s[i-1]+b[i];
RMQ();
//长度小于等于128,暴力判断
ans=0LL;
for(len=1;len<=128;len++)
{
for(l=1;l+len-1<=n;l++)
{
r=l+len-1;
mx1=Mx(l,r);
if((s[r]-s[l-1])%2==0&&mx1*2<=s[r]-s[l-1])ans++;
}
}
//长度大于128,sum一定大于等于max*2,只需要判断sum是否是偶数
memset(js,0,sizeof(js));
memset(os,0,sizeof(os));
for(i=n;i>=0;i--)
{
js[i]+=js[i+1];os[i]+=os[i+1];
if(s[i]%2==0)os[i]++;
else js[i]++;
}
for(l=1;l<=n;l++)
{
l1=l-1;r=l+128-1;
if(r+1<=n)
{
if(s[l1]%2==0)
{
ans+=(LL)os[r+1];
}
else
{
ans+=(LL)js[r+1];
}
}
}
printf("%lld",ans);
return 0;
}