zoukankan      html  css  js  c++  java
  • BZOJ 2728 HNOI2012 与非 高斯消元

    题目大意:给定k位二进制下的n个数,求[l,r]区间内有多少个数能通过这几个数与非得到

    首先观察真值表 我们有A nand A = not A

    然后就有not ( A nand B ) = A and B

    与和非都弄到了,我们就能够做出一切逻辑运算了,比方说或和异或

    A or B = not ( ( not A ) and ( not B ) )

    A xor B = ( A or B ) and ( A nand B )

    然后我们对于位运算能够发现一个性质

    对于某两位来说。假设对于每个数。这两位上的值都是同样的,那么这两位不管怎么计算终于结果都会是同样的

    比方说10(1010)和7(0111),第一位和第三位都是同样的。所以最后不管怎么计算,这两位都是一样的

    然后我们这么处理:

    对于每一位,我们枚举每个数,若该数该位上为0。我们就对这个数取非

    然后把全部数取与

    该位上都是1,所以取与后一定是1。对于其它位,仅仅要有这两位不同的数存在,那么这位一定是0

    最后取与的结果中与该位所有同样的位都是1,其余都是0

    对于每一位这样处理,标记去重,然后能够得到线性基,保证每一位存在且仅存在于线性基中的一个数上

    拿去从大到小贪心处理就可以 得到二进制序列即为答案

    此题有坑 题目描写叙述中1<=L<=R<=10^18 可是第七个点L=0 坑死一票人啊

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define M 1010
    using namespace std;
    typedef long long ll;
    int n,k;
    ll digit,l,r,a[M],basis[70],tot;
    bool v[70];
    ll Get_Digit(ll x)
    {
    	if(x==-1)
    		return -1;//坑比!!! 
    	ll now=0,re=0;
    	int i;
    	for(i=1;i<=tot;i++)
    		if( (now|basis[i])<=x )
    			now|=basis[i],re|=(1ll<<tot-i);
    	return re;
    }
    int main()
    {
    	
    	//freopen("nand.in","r",stdin);
    	//reopen("nand.out","w",stdout);
    	
    	int i,j;
    	ll now;
    	cin>>n>>k>>l>>r;
    	digit=(1ll<<k)-1;
    	for(i=1;i<=n;i++)
    		scanf("%lld",&a[i]);
    	for(i=k-1;~i;i--)
    		if(!v[i])
    		{
    			now=digit;
    			for(j=1;j<=n;j++)
    				if( a[j]&(1ll<<i) )
    					now&=a[j];
    				else
    					now&=~a[j]&digit;
    			basis[++tot]=now;
    			for(j=0;j<=i;j++)
    				if( now&(1ll<<j) )
    					v[j]=1;
    		}
    	cout<<Get_Digit(r)-Get_Digit(l-1)<<endl;
    }
    //lld
    


  • 相关阅读:
    第四次作业
    团队编程第三次博客
    团队编程2
    团队编程
    ARM寄存器总结:
    proc介绍及问题分析
    Ubuntu连接手机步骤
    Bluetooth(android 4.2.2版本)
    Android Bluetooth 总结
    android代码常识
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/7098380.html
Copyright © 2011-2022 走看看