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
110 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:%505020 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...