您的位置:首页 > 其它

与非

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$考虑

#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: