题目链接: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;
}