BZOJ 2728 HNOI2012 與非 高斯消元
阿新 • • 發佈:2017-06-30
sca 邏輯 都是 -- cstring 位運算 不同的 ret asi
題目大意:給定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
BZOJ 2728 HNOI2012 與非 高斯消元