题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4737
题目意思:
有n个数,求区间L,R的个数使得该区间内所有数或的值小于m.
解题思路:
这题比赛的时候暴力过的。数据太水不解释。
这题可以利用单调性降低时间复杂度。
分段来求,依次找到以j结束的恰好构成或值小于m的i,然后剔除包括该区间【i,j】的所有区间。
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#define eps 1e-6
#define INF 0x3fffffff
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define Maxn 110000
//单调性思想 根据单调性 降低复杂度 由于是或运算,所以保存位
int bit[35],num[35],sa[Maxn]; //bit[i]=2^i
int main()
{
int t,n,m;
bit[0]=1;
for(int i=1;i<=31;i++)
bit[i]=bit[i-1]*2;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&sa[i]);
int i=1,j=1,last=1;
memset(num,0,sizeof(num));
ll ans=(ll)n*(n+1)/2; //注意是+1,因为当区间为一个点时,只计算了一次
while(j<=n)
{
for(int k=0;k<=31;k++)
if(sa[j]&(1<<k))
num[k]++; //有可能有多个1,不能直接赋值为1
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++)
if(sa[i]&(1<<k))
num[k]--; //有可能有多个1
sum=0;
for(int k=0;k<=31;k++)
if(num[k])
sum+=bit[k];
i++;
}
//printf(":::%d %d\n",i,j);
ans-=(ll)(i-last)*(n-j+1); //减去前一段和后一段
last=i;//注意分段,不能重复
}
j++;
}
printf("Case #%d: %I64d\n",ca,ans);
}
return 0;
}