2013-06-02 127 views
2

我試圖理解這段代碼。我認爲它使用了段樹數據結構的一些變體,但我無法理解此代碼中的任何位操作。翻轉硬幣的位操作

的實際問題是,有N個硬幣,我們有1個動作和1個查詢,該查詢是

  1. 要在範圍翻轉硬幣[A,B]
  2. 爲了找到頭在範圍的數量[A,B]。

#define LEAVES (131072) 
#define MSB_V (0x80000000) 
#define MSB_P (31) 

int it[2 * LEAVES]; 
int A, B; //Query range 

void flip (int i, int min, int max) 
{ 
    if ((min > B) || (max <= A)) 
    { } 
    else if ((min >= A) && ((max-1) <= B)) 
    { it[i] ^= MSB_V; } 
    else 
    { 
     int l = 2 * i; 
     int r = l + 1; 
     int mid = (min + max)/2; 
     it[l] ^= it[i] & MSB_V; 
     it[r] ^= it[i] & MSB_V; 
     it[i] ^= MSB_V; 
     flip(l, min, mid); 
     flip(r, mid, max); 
     it[i] = (it[l] >> MSB_P ? mid - min - (it[l]^MSB_V) : it[l]) + 
       (it[r] >> MSB_P ? max - mid - (it[r]^MSB_V) : it[r]); 
    } 
} 

int count (int i, int min, int max) 
{ 
    int h; 
    if ((min > B) || (max <= A)) 
    { h = 0; } 
    else if ((min >= A) && ((max-1) <= B)) 
    { h = it[i] >> MSB_P ? max - min - (it[i]^MSB_V) : it[i]; } 
    else 
    { 
     int l = 2 * i; 
     int r = l + 1; 
     int mid = (min + max)/2; 
     it[l] ^= it[i] & MSB_V; 
     it[r] ^= it[i] & MSB_V; 
     it[i] ^= MSB_V; 
     it[i] = (it[l] >> MSB_P ? mid - min - (it[l]^MSB_V) : it[l]) + 
       (it[r] >> MSB_P ? max - mid - (it[r]^MSB_V) : it[r]); 
     h = count(l, min, mid) + count(r, mid, max); 
    } 
    return h; 
} 

有人可以請給我什麼是所有這些位操作背後的邏輯

+2

看起來這只是XOR'ing位來翻轉它們。所以每個硬幣都有一個單獨的位,0/1頭/尾。 – Sysyphus

+0

這個代碼在兩個函數中都有相同的錯誤:語句'it [i]^= MSB_V;'後面跟着一個賦值'it [i] =(與舊值無關的東西[i])' –

回答

4

it代表一個完整的二叉樹一些提示;節點1是根,節點k的子節點是2k和2k + 1。

完整的二叉樹的葉子是硬幣。內部節點是面向特定方式(低31位)的硬幣數量和「翻轉」標記(在符號位中)。如果翻轉的市場清晰,則低31位計算節點子樹中擡頭硬幣的數量,而如果翻轉位被設置,則計數尾部硬幣。

雙方findcount在此使用的參數約定是i是被計數或翻轉的節點,min是由該節點所表示的最低索引,並且max是最高的索引。在findcount中的前兩個測試檢查翻轉/計數範圍(由AB定義)是否與節點i的範圍不相交或包含節點i的範圍。

flip看到的是:

  • 如果[A,B]是[最小值,最大值]脫節,什麼也不做。
  • 如果[A,B]包含[min,max],則翻轉翻轉的標記。
  • 否則,對兩個孩子運行flip。一旦我們完成了這個,這裏的不變量就會被破壞;通過在兩個孩子中增加擡頭硬幣的數量來修復它。

count看到的是:

  • 如果[A,B]是[最小值,最大值]不相交,返回0
  • 如果[A,B]包含[分鐘,最大值],返回該節點的計數。
  • 否則,加起來我們孩子的數量。
+0

在翻轉函數'it [l]^= it [i]&MSB_V;它[r]^= it [i]&MSB_V;它[i]^= MSB_V;'這些陳述背後的邏輯是什麼?如果它[i]正在計算擡頭,那麼它的第31位保留,否則翻轉,與[r]一樣,然後它[i]被翻轉,但爲什麼我們需要這個?不,我們只是做遞歸調用,然後適當地計算它[我]在它的幫助下[l],它[r] –

+0

我還有一個疑問,如果翻轉函數不遞歸下降到葉節點並停止在一些中間節點,那麼中間節點下面的所有節點都不會翻轉。例如如果N = 4,A = 1且B = 4,並且flip(1,0,N)被調用,所以它將翻轉根節點並且將返回而不翻轉其下的節點。 你能解釋一下嗎?我在想什麼錯了? –

+0

翻轉是懶洋洋地完成的。我們跟蹤是否所有的子樹的硬幣都被翻轉過。你提到的代碼片段「推」一個懶惰的翻轉從一個節點到它的孩子。 – tmyklebu