题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2728
思路
其实这个与非真的很神奇啊,它可以覆盖与、或、非、亦或四种运算,这个可以手玩出来(本渣太懒没试过。。。)。那么这样的话,不管
Ai
中某一位是0还是1,每个
Ai
选了多少次,最终的答案中的这一位既有可能是1,也有可能是0,但是某个数是否能被与非出来还是有限制的,观察到如果对于任意的
Ai,1<=Ai<=n
而言,
Ai(2)[a]=Ai(2)[b](Ai的第a位和第b位相同)
,那么最终无论如何与非,得到的答案的第
a和b
位一定是相同的,这个也可以手玩出来(本渣太懒没试过=_=b)。
那么我们可以暴力枚举1到k上的两位
a,b,a<b
(反正k很小,
k2
复杂度几乎可以无视掉),通过并查集合并掉所有相同的列(为了便于叙述,以下均称相同的列组成的玩意是集合),
belong[i]=二进制上第i位
所在的集合。同时可以求出
num=
集合的个数。
然后我们就需要求区间
[L,R]
上能被与非出来的数的个数了。很显然可以想到只需能求出区间
[1,x−1]
的合法数个数就行了,写个函数
solve(x)
来求。
n[L,R]([L,R]能被与非出来的数的个数)=solve(R+1)−solve(L)
那我们现在只需要关心
solve(x)
的过程了,比较显然的是由于最终可以与非出来的数的个数最多只能是
2num
(每个集合可以选1或者0,有2种选法,一共
num
个集合)。而由于限制了二进制数的长度最大为
k
,因此
那么现在的
x
就是合法的了,
如果
x 的当前位所属集合是0还是1尚未确定
(1). x 是1(2).
x 是0
标记 x 的当前位所属集合确定是0- 否则
x 的当前位所属集合是0还是1已经确定了
(1). x 的当前位是1,但是之前确定了它所在的集合都是0,直接退出
(2).x 的当前位是0,但是之前确定了它所在的集合都是1,直接退出代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 1010 #define MAXK 70 using namespace std; typedef long long int LL; LL a[MAXN]; //保存A1...An LL L,R; int n,k,num=0; //num=集合总数 bool isSame(int x,int y) //判断n个Ai中,对于每个Ai,它的第x位和第y位是否都相同 { for(int i=1;i<=n;i++) if(((a[i]>>x)^(a[i]>>y))&1) return false; return true; } int belong[MAXK]; //belong[i]=与列i相同的列j,j<i,j=i表示列i是独立不受影响的 int mark[MAXK]; //mark[i]=-1表示尚未确定集合i是什么数字,0表示确定集合i中的每一位都是0,1表示确定每一位都是1 LL solve(LL x) //求区间[1,x-1]的能与非出来的数字个数 { if(x>=1LL<<k) return 1LL<<num;//x的长度超出了k int tmp=num; //tmp=有多少组集合在1~i位中未确定的 LL ans=0; memset(mark,-1,sizeof(mark)); for(int i=k-1;i>=0;i--) //枚举x的第i位 { if(mark[belong[i]]==-1) //第i位所属集合没定,第i位是几,第i位所属的集合也定为这个数字 { tmp--; if((x>>i)&1) //x的第i位是1 { mark[belong[i]]=1; ans+=1LL<<tmp; } else mark[belong[i]]=0; } else { if((x>>i)&1) { if(mark[belong[i]]==0) { ans+=1LL<<tmp; break;//x的第i位是1,但是x所属集合已经被确定为了0,直接退出 } } else if(mark[belong[i]]==1) break; //x的第i位是0,但是x所属集合已经被确定为了1,直接退出 } } 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(isSame(i,j)) //n个A数字中i位和j位都是一样的 { belong[i]=j; flag=true; break; } if(!flag) { num++; belong[i]=i; } } printf("%lld\n",solve(R+1)-solve(L)); return 0; }