题目链接:https://vjudge.net/contest/308832#problem/I
题目大意:有n个数,求区间L,R的个数使得该区间内所有数或的值小于m.的区间[L, R]的个数,L<=R。
思路:
因为数字或完之后,只可能大于等于本身,不可能变小,因为1|1=1 ; 1|0 = 1; 0|0 = 0;
所以我们可以用尺取法得到满足条件的区间,如果用尺取计数,会重复。
所以容斥一下:用总的组合个数-不满足的组合个数=满足条件的区间个数。
还有对区间或值的计算,非常巧妙。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
int a[100010];
int bit[32];
int num[32];
int n, m;
int main()
{
bit[0]=1;
for(int i=1;i<=31;i++)
{
bit[i]=bit[i-1]*2;
}
int T, CUT=1;
scanf("%d",&T);
while(T--)
{
memset(num, 0, sizeof(num));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int L=1, R=1, L0=1;
LL ans=1ll*n*(n+1)/2;
while(R<=n)
{
for(int k=0;k<31;k++)//加上a[R]
{
if(a[R]&(1<<k))
{
num[k]++;
}
}
int sum=0;
for(int k=0;k<31;k++)
{
if(num[k])
{
sum+=bit[k];
}
}
if(sum>=m)
{
while(sum>=m)
{
for(int k=0;k<31;k++)//减去a[L]
{
if(a[L]&(1<<k))
{
num[k]--;
}
}
sum=0;
for(int k=0;k<31;k++)
{
if(num[k])
{
sum+=bit[k];
}
}
L++;
}
ans-=1ll*(L-L0)*(n-R+1);//容斥
L0=L;
}
R++;
}
printf("Case #%d: %lld\n",CUT++,ans);
}
return 0;
}