传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2728
思路:首先我们要玩出nand的性质
nand可以表示出所有逻辑运算
not a=a nand a
a and b=not (a nand b)
....
这题另一个性质就是如果a[1]~a[n]的所有数第i位和第j位相同,那么nand出来的数第i位和第j位也相同
把取值相同的并到一起,用一个并查集维护一下。
然后我们就要实现一个函数query(x)表示0-x之间有多少个数可以得到
从x的高位向低位做,
如果x该位为1,我们有两种选择
不选这个1,那低位可以任意选,方案+=2^p,不用往下做了 p为还未确定的集合数
选了这个1,低位还不能任意选,继续往下做
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=1010,maxk=70;
typedef long long ll;
using namespace std;
int n,k,bel[maxk],cnt,mark[maxk];ll L,R,a[maxn];
int find(int x){return x==bel[x]?x:bel[x]=find(bel[x]);}
bool check(int x,int y){
for (int i=1;i<=n;i++)
if (((a[i]>>x)^(a[i]>>y))&1) return 0;
return 1;
}
ll query(ll x){
if (++x>=(1ll<<k)) return 1ll<<cnt;
int tmp=cnt;ll res=0;memset(mark,-1,sizeof(mark));
for (int i=k-1;i>=0;i--){
if ((x>>i)&1){
if (mark[find(i)]!=1){
if (mark[find(i)]==-1) tmp--,mark[find(i)]=1;
res+=1ll<<tmp;
if (!mark[find(i)]) break;
}
}
else{
if (mark[find(i)]==-1) tmp--,mark[find(i)]=0;
else if (mark[find(i)]==1) break;
}
}
return res;
}
int main(){
scanf("%d%d%lld%lld",&n,&k,&L,&R);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=0;i<k;i++) bel[i]=i;
for (int i=0;i<k;i++) for (int j=0;j<i;j++)
if (check(i,j)){bel[find(bel[i])]=find(j);break;}
for (int i=0;i<k;i++) if (find(i)==i) cnt++;
//printf("%d\n",cnt);
//for (int i=0;i<k;i++) printf("%d %d\n",i,bel[i]);
printf("%lld\n",query(R)-query(L-1));
return 0;
}
/*
5 60 0 1234567891012
1234567891277 5674897451677 1895415618481 8741189478971 2106156486449
*/