您的位置:首页 > 其它

【bzoj2728】[HNOI2012]与非

2016-03-25 21:00 260 查看
先打出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;

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