我们可以先按位处理把每一个位置上是0还是1处理一下
通常在判断区间个数的做法的时候我们通常的做法是先固定一个左端点,然后二分右边端点
那怎么写二分的check函数呢?
我们看手玩一下样例
我们可以知道and运算的性质要想让and运算和为0说明这些数的每一个位置and完都是0
那我们知道在第i位上只要有一个数为0那么这些数字and运算完之后就是0
那我们只需要二分出最大右端点的最小左位置那就可以了
我们设置这个答案为mid
那mid-n这部分都是满足的
那每个左端点对应的答案就是ans+=(n-l+1);
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll n,m;
ll a[N],vis[N][32];
//vis[i][j]表示第i个数字的第j位是多少
bool flag[N];
queue<int>q;
bool check(int l,int r)
{
for(int i=0;i<32;i++)
{
if(vis[r][i]-vis[l-1][i]==(r-l+1))return false;
//说明第i个位置上面l-r都是1这样j位上的区间和才是区间长度
}
return true;
}
void cf()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=0;i<32;i++)
{
for(int j=1;j<=n;j++)
{
vis[j][i]=vis[j-1][i];
if((a[j]>>i)&1)
{
vis[j][i]++;
}
}
}
ll ans=0;
for(int i=1;i<=n;i++){
int l=i;
int r=n+1;
while(l<r)
{
int mid=l+r>>1;
if(check(i,mid))
{
r=mid;//找到符合条件的最左边端点
}
else l=mid+1;
}
ans+=(n-l+1);
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll t;
t=1;
while(t--)
{
cf();
}
}