Solution
这题还是不错的。
首先可以观察到的是,这个与非的功能还是十分强大的,他可以实现按位取反
x
N
A
N
D
x
x\ NAND\ x
x NAND x,下面按位取反的符号设为
!
!
!,或
(
!
x
)
N
A
N
D
(
!
y
)
(!x)NAND(!y)
(!x)NAND(!y),与
!
(
x
N
A
N
D
y
)
!(x\ NAND\ y)
!(x NAND y),异或
(
x
∣
y
)
&
(
x
N
A
N
D
y
)
(x|y)\&(x\ NAND\ y)
(x∣y)&(x NAND y),这些基本的位运算。
然后要求
[
L
,
R
]
[L,R]
[L,R]范围内多少数能表示出来,考虑构造一个类似线性基的东西。
从高到低考虑每一位,怎么搞出这一位的线性基,使得位与位之间尽量互不影响。对于每个数,如果这一位不是
1
1
1,就按位取反,然后把
n
n
n个数再与起来,这样首先可以保证这一位是
1
1
1,然后若其它位至少有一位不同就是
0
0
0,这样就得到了每一位的线性基。
最后再像数位DP那样,一位一位确定计算方案即可。
Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=1010;
const int inf=2147483647;
LL read()
{
LL x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,k,c[62],tot=0;LL L,R,a[Maxn],b[62],o;
LL query(LL x)
{
if(x<0)return -1;
LL re=0,y=0;
for(int i=1;i<=tot;i++)
if((y|b[i])<=x)re+=(1LL<<(tot-i)),y|=b[i];
return re;
}
int main()
{
n=read(),k=read(),L=read(),R=read();
o=(1LL<<k)-1;
for(int i=1;i<=n;i++)a[i]=read();
for(int i=k-1;i>=0;i--)
if(!c[i])
{
b[++tot]=o;
for(int j=1;j<=n;j++)
if((1LL<<i)&a[j])b[tot]&=a[j];
else b[tot]&=((~a[j])&o);
for(int j=i;j>=0;j--)
if((1LL<<j)&b[tot])c[j]=tot;
}
printf("%lld",query(R)-query(L-1));
}