题意
给定长度为
n
n
的数列 ,定义
f(i,j)=ai|ai+1|...|aj−1|aj
f
(
i
,
j
)
=
a
i
|
a
i
+
1
|
.
.
.
|
a
j
−
1
|
a
j
。求满足
f(i,j)<m
f
(
i
,
j
)
<
m
的有序数对
(i,j)
(
i
,
j
)
的对数。
1≤n≤100000
1
≤
n
≤
100000
1≤m,ai≤230
1
≤
m
,
a
i
≤
2
30
思路
首先一个数或上另一个数,大小只可能变大,由此可以找到单调性。我们把原题变为求 f(i,j)≥m f ( i , j ) ≥ m 的 (i,j) ( i , j ) 的对数。那在求得一个满足条件的区间后,右端点更右的区间均满足条件,为了实时更新,我们再开一个 cnt c n t 数组记录目前每个位上累计 1 1 <script type="math/tex" id="MathJax-Element-22">1</script> 的个数。有了这些就可以跑尺取了。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
int a[100003],lth[100003],cnt[33];
bool bit[100003][33];
int main()
{
int T;
scanf("%d",&T);
FOR(Ti,1,T)
{
memset(cnt,0,sizeof(cnt));
memset(lth,0,sizeof(lth));
int n,m;
scanf("%d%d",&n,&m);
FOR(i,1,n)scanf("%d",&a[i]);
FOR(i,1,n)
{
int k=a[i];
while(k)
{
bit[i][lth[i]++]=k&1;
k>>=1;
}
}
int R=0,s=0;
LL ans=0;
FOR(L,1,n)
{
while(R<=n&&s<m)
{
R++;
FOR(i,0,lth[R]-1)
{
if(!cnt[i]&&bit[R][i])s+=1<<i;
cnt[i]+=bit[R][i];
}
}
if(R>n)break;
ans+=n-R+1;
FOR(i,0,lth[L]-1)
{
cnt[i]-=bit[L][i];
if(!cnt[i]&&bit[L][i])s-=1<<i;
}
}
printf("Case #%d: %lld\n",Ti,1LL*n*(n+1)/2-ans);
}
return 0;
}