皮卡丘的夢想2(線段樹 二進位制狀態壓縮)

2022-01-11 13:44:04 字數 3188 閱讀 9381

description

一天,乙隻住在 501 實驗室的皮卡丘決定發奮學習,成為像 leiq 一樣的巨巨,於是他向鎮上的賢者金桔請教如何才能進化成乙隻雷丘。

金桔告訴他需要進化石才能進化,並給了他乙個地圖,地圖上有 n 個小鎮,他需要從這些小鎮中收集進化石。

接下來他會進行 q 次操作,可能是打聽進化石的資訊,也可能是向你詢問第 l 個小鎮到第 r 個小鎮之間的進化石種類。

如果是打聽資訊,則皮卡丘會得到乙個小鎮的進化石變化資訊,可能是引入了新的進化石,也可能是失去了全部的某種進化石。

如果是向你詢問,你需要回答他第 l 個小鎮到第 r 個小鎮之間的進化石種類。

input

首先輸入乙個整數 t (1 <= t <= 10),代表有 t 組資料。

每組資料的第一行輸入乙個整數 n (1 <= n <= 100000) 和乙個整數 q (1 <= q <= 100000),分別代表有 n 個小鎮,表皮卡丘有 q 次操作。

接下來輸入 q 行,對於每次操作,先輸入操作型別,然後根據操作型別讀入:

output

對於每組輸入,首先輸出一行 "case t:",表示當前是第幾組資料。

對於每組資料中的每次 3 操作,在一行中按編號公升序輸出所有可收集的進化石。如果沒有進化石可收集,則輸出乙個 meik 的百分號 "%"(不包括引號)。

sample input

1

10 10

3 1 10

1 1 50

3 1 5

1 2 20

3 1 1

3 1 2

2 1 50

2 2 20

3 1 2

3 1 10

sample output

case 1:%50

5020 50

%%

解題思路:剛好暑假學完了線段樹,我一開始就把這道題當做線段樹的裸題,用線段樹來維護資料,不就是單點更新,區段查詢嘛。但是很不幸時間超限,後來我考慮了原來每次查詢進化石的時候還是要全部遍歷一遍,所以是要超時的。這時我們就需要更高效的方法來儲存和查詢所有進化石的狀態,這種方法就是利用二進位制進行狀態壓縮。

首先我們要明確一點加入的是第幾種進化石,而不是幾塊進化石,也就是看的是存不存在,而不是存在的數量!!

我們用二進位制數的每一位來表示每種進化石的有無,也即該小鎮進化石的存在狀態。例如,對於有 1、4 號進化石的小鎮,我們可以用二進位制數 1001 來表示。它的含義是:從右向左依次表示第 1 個到第 n 個進化石的有無,1 表示有,0 表示無。而且很容易想到,由於每一位只有 0 或 1 兩種可能,且每一位都對應固定的編號,所以對於任意乙個二進位制數,都能保證唯一對應一種存在狀態。

解決了如何表示存在狀態的問題,下一步就是如何儲存了。例如,當前我們的進化石存在狀態為:1、4,對應二進位制 1001,如果我們加入乙個 3 號進化石,則應變為 1101,也就是讓倒數第三位變成 1。這裡需要用到位運算:對於 1001,我們讓它與 0100(只含有 3 號石的狀態)進行或運算,即兩數對應的位有乙個或兩個為 1 時結果為1,否則為 0,運算結果為 1101。這樣我們使用或運算就可以實現兩個狀態的合併。至於如何表示單個進化石的狀態,很簡單,使用左移運算就可以了,例如:表示 3 號石存在,只需將 1(0001)左移 3-1=2 位即得到 0100。

這樣,我們只需要把二進位制和線段樹結合一下就可以愉快地告別 tle 了。在儲存時,每乙個結點都表示它的左右子結點的合併狀態,即對左右子結點進行或運算後的結果,而葉結點直接儲存狀態。在查詢時,只需要遍歷結果對應二進位制的每一位來輸出即可。

1 #include2 #include3 #include4

#define ll long long int

5const

int maxn=1e5+10;6

using

namespace

std;

7int

n,q;

8 ll sum[maxn<<2];9

void push_up(int i)///

向上回溯,狀態合併

1013

void build(int l,int r,int rt)///

建樹14

20int mid=(r+l)>>1

;21 build(l,mid,rt<<1

);22 build(mid+1,r,rt<<1|1

);23

push_up(rt);24}

25void add(int l,int r,int pos,int v,int

rt)26

32int m=(r+l)>>1;33

if(m>=pos)

3437

else

3841

push_up(rt);42}

43void del(int l,int r,int pos,int val,int

rt)44

50int mid=(r+l)>>1;51

if(pos<=mid)

5255

else

5659

push_up(rt);60}

61 ll query(int l,int r,int l,int r,int rt)///

區間查詢

6267 ll ans=0;68

int mid=(r+l)>>1;69

if(l<=mid)

7073

if(r>mid)

7477

return

ans;78}

7980

intmain()

8198

if(op==2)99

102if(op==3

)103

116else

117120 printf("%d"

,ot);

121}

122 ot++;

123 ans>>=1

;124

}125

if(flag)

126129

else

130133

}134

}135

}136

return0;

137 }

sdutoj 皮卡丘的夢想2 狀態壓縮 線段樹

題目鏈結 思路 我開了60個樹狀陣列水過去了.但是資料在大點就不行了 正解應該是線段樹 狀態壓縮吧,將60個寶石每一種壓縮成乙個二進位制位.這樣向上回溯的時候記錄兩個子樹或就行了.includeusing namespace std define lson l,m,node 1 define rso...

XOR on Segment(線段樹,二進位制拆位)

題目描述 看到異或我們很自然地想到二進位制拆位。我們對於線段樹的每個節點,建乙個陣列sum sumsu m。s um i sum i sum i 表示當前節點的兒子中有多少個數的二進位制有第i位 因為這題是區間修改,所以再建乙個laz y i lazy i lazy i 表示懶標記 再對線段樹的 稍...

POJ2777解題報告 線段樹,二進位制儲存狀態

題目概述 給一塊長度為l的板子,有兩種操作,第一種將a到b刷成顏色c,第二種詢問a到b一共有多少種顏色。顏色數小於等於30。大致思路 首先很容易發現線段樹可以解決問題,不過怎麼儲存顏色呢?我們發現顏色總數很少,於是可以為2進製位來表示顏色,問題就可以很好地解決了。include include in...