zoukankan      html  css  js  c++  java
  • 与非

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

      通过题目就可以看出$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
    ",solve(r+1)-solve(l));
    54     return 0;
    55 }
    View Code
  • 相关阅读:
    Ubuntu 11.04 安装后要做的20件事情
    Net 服务命令行参考之一
    Openerp约束句型
    Ubuntu进入Shell
    postgreSql基础命令及linux下postgreSql命令
    解决中文乱码的问题
    An error occured while handling a json request
    Java Socket编程
    CentOS 7 中 Docker 的安装和卸载
    Spring Boot整合shiro-登录认证和权限管理
  • 原文地址:https://www.cnblogs.com/Ngshily/p/5112279.html
Copyright © 2011-2022 走看看