与非
2016-01-08 10:27
190 查看
对于一个乱七八糟的位运算,我们可以通过真值表及它的性质把它转化普通的位运算
通过题目就可以看出$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$考虑
View Code
通过题目就可以看出$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$考虑
#include<bits/stdc++.h> using namespace std; #define maxn 1005 #define maxk 65 typedef long long i64; int n,k,num,vis[maxk],bel[maxk]; i64 l,r,a[maxn]; bool same(int x,int y){ for(int i=1;i<=n;i++) if(((a[i]>>x)&1)^((a[i]>>y)&1))return false; return true; } i64 solve(i64 x){ if(x>=(1LL<<k))return 1LL<<num; i64 ans=0; int rst=num; memset(vis,-1,sizeof(vis)); for(int i=k-1;i>=0;i--){ if(vis[bel[i]]==-1){ rst--; if(x>>i&1){ vis[bel[i]]=1; ans+=(1LL<<rst); } else vis[bel[i]]=0; } else{ if(x>>i&1){ if(!vis[bel[i]]){ ans+=(1LL<<rst); break; } } else if(vis[bel[i]])break; } } return ans; } 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++){ bool flag=false; for(int j=0;j<i;j++) if(same(i,j)){ bel[i]=j; flag=true; break; } if(!flag)num++,bel[i]=i; } printf("%lld\n",solve(r+1)-solve(l)); return 0; }
View Code
相关文章推荐
- 三、git rebase
- MySqlDataAdapter类
- jvisualvm远程调用tomcat测试性能
- iOS UITextField的leftView
- 2叉树遍历的递归实现-使用c++模板
- 如何缩小或者扩大数据文件 How to Resize a Datafile (Doc ID 1029252.6)
- NSDictionary基础-创建、初始化、赋值、获取、复制
- mabatis--查询缓存
- UIActivityIndicatorView的使用
- MAC图片格式转换
- MyMusic
- iOS 打印日志显示系统详细时间,类名,行号及打印值
- (.我爱你)域名2015年度报告:商务中国一枝独秀
- pip和pip3同时使用
- Leetcode222: Jump Game II
- AP1501的输入滤波电容烧坏原因分析
- 基于servlet的文件下载
- 【Android基础】Android WebView参数设置详解
- HTML5之---Manifest 文件--离线缓存
- 使用gradle做第一个java项目