与非

对于一个乱七八糟的位运算,我们可以通过真值表及它的性质把它转化普通的位运算

  通过题目就可以看出$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 }
View Code

 

posted @ 2016-01-08 10:27  Ngshily  阅读(403)  评论(0编辑  收藏  举报