与非
对于一个乱七八糟的位运算,我们可以通过真值表及它的性质把它转化普通的位运算
通过题目就可以看出$A nand B=not (A and B)$
所以 $not A=A nand A$
$A and B=not (A nand B)$
$A or B=not ((not A) and (not B))$
$A xor B=(A or B) and (not (A and B))$
于是运算就有了较大的任意性,也就是说每一位都可以是$0$或$1$,我们可以$nand$出一定长度以内的任意数
然而我们会发现一个限制:我们把$n$个数化成二进制写成$n$行,如果有两列数完全一样,那么无论我们怎么$nand$所得到的结果的这两位一定是一样的
所以其实我们能得到的是满足限制条件的所有数
具体做法是,对于每一列,如果某个数的这一列为$0$,则把该数取$not$,然后把$n$个数$and$在一起,这样就得到了只有相同位为$1$的基
所以我们可以用并查集把所有一样的列∪起来,然后数位DP:
定义函数$solve(x)$返回$[0,x-1]$之间的满足条件的数的个数,则答案为$solve(r+1)-solve(l)$
若$x>=2^{k}$,而$0<=Ai<=2^{k}-1$,所以假设此时有$num$个不同的列的集合,则始终返回$2^{num}$
否则从高到低枚举第一个与$x$不同的位,然后分两种情况
①若该位所在的集合的值未被确定,$ans+=(2^{未被确定的集合个数})$
②若该位所在的集合已被确定,且x的当前与确定的值不同,$break$
具体细节还要根据x的该位是$0$还是$1$考虑
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define maxn 1005 4 #define maxk 65 5 typedef long long i64; 6 int n,k,num,vis[maxk],bel[maxk]; 7 i64 l,r,a[maxn]; 8 bool same(int x,int y){ 9 for(int i=1;i<=n;i++) 10 if(((a[i]>>x)&1)^((a[i]>>y)&1))return false; 11 return true; 12 } 13 i64 solve(i64 x){ 14 if(x>=(1LL<<k))return 1LL<<num; 15 i64 ans=0; 16 int rst=num; 17 memset(vis,-1,sizeof(vis)); 18 for(int i=k-1;i>=0;i--){ 19 if(vis[bel[i]]==-1){ 20 rst--; 21 if(x>>i&1){ 22 vis[bel[i]]=1; 23 ans+=(1LL<<rst); 24 } 25 else vis[bel[i]]=0; 26 } 27 else{ 28 if(x>>i&1){ 29 if(!vis[bel[i]]){ 30 ans+=(1LL<<rst); 31 break; 32 } 33 } 34 else if(vis[bel[i]])break; 35 } 36 } 37 return ans; 38 } 39 int main(){ 40 scanf("%d%d%lld%lld",&n,&k,&l,&r); 41 for(int i=1;i<=n;i++) 42 scanf("%lld",&a[i]); 43 for(int i=0;i<k;i++){ 44 bool flag=false; 45 for(int j=0;j<i;j++) 46 if(same(i,j)){ 47 bel[i]=j; 48 flag=true; 49 break; 50 } 51 if(!flag)num++,bel[i]=i; 52 } 53 printf("%lld\n",solve(r+1)-solve(l)); 54 return 0; 55 }