【bzoj2728】[HNOI2012]与非
先打出nand表
0 nand 0=1
1 nand 1=0
0 nand 1=1
1 nand 0=1
容易发现(!a)=a nand a
然后(a&b)=!(a nand b)
然后(a|b)=!((!a)&(!b))
然后(a^b)=(a|b)&(a nand b)
所以通过nand我们可以实现任意一种位运算
所以每一位我们想得到0/1都是可以的
按道理[L,R]中符合位数要求的数都能得到
然而我们发现这样一个显然的结论:
如果A序列中每个数的第i位和第j位都相等,那么不论怎么nand,我们得到的数的第i位和第j位一定会相等。
不妨设这种限制条件为(i,j)。
于是我们统计[L,R]中符合这个限制条件的数即可。
首先从高位找x的第一个1,进行决策。
如果我们不选这个1,那么剩下的未决策位可以随便取,答案加上2^未决策位数。
如果我们选这个1,那么从这个1开始接下来一段的0必须取0(取1的话一定是大于R的数),于是直接考虑下一个1的决策。
再考虑限制条件。我们设xd[i]=符合(i,j)限制的最小j,那么决策第i位为1时,我们也会决策第xd[i]位必须为1。必须取0的决策同理。
如果准备决策某一位,发现其已经被限制,则我们需要判断一下,如果已有限制与当前要给它的决策一致,则不用管了。如果希望决策它为1但它必须为0,那么这一位我们只能决策它为0,加上答案,后面的1都不用再枚举了。如果希望决策它为0但它必须为1,那么说明没办法决策出符合要求的数,直接break。
这样得到的是[0,x-1]中符合要求的数,因为我们不选第一个1就可以决策到0,然而我们决策完最后一个1也决策不出x。
答案就是query(r+1)-query(l)。
(以上来自XZY大神犇空间。。。)
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> using namespace std; typedef long long LL; LL k,n,l,r; LL a[1010<<2]; int s[110],xd[110]; int res; bool flag(true); bool check(int i,int j) { for (int m=1;m<=n;m++) if (((a[m]>>i) ^ (a[m]>>j)) & 1) return false; return true; } LL query(LL x) { LL ans=0; if (x>=(1LL<<k)) return 1LL<<res; int u,v=res; for (int i=0;i<=64;i++) s[i]=2; for (int i=k-1;i>=0;i--) { u=xd[i]; if ((x>>i) & 1) { if (s[u]==2) s[u]=1,v--,ans+=(1LL<<v); else if (s[u]==0) { ans+=(1LL<<v); break; } } else if (s[u]==2) s[u]=0,v--; else if (s[u]==1) break; } return ans; } int main() { scanf("%lld%lld%lld%lld",&n,&k,&l,&r); for (int i=1;i<=n;i++) scanf("%lld",&a[i]); for (int i=0;i<=k-1;i++) { int j; for (j=0;j<=i-1;j++) if (check(i,j)) { xd[i]=j; break; } if (i==j) xd[i]=i,res++; } LL s1=query(r+1); LL s2=query(l); printf("%lld",s1-s2); return 0; }