學了三天主席樹,思維**了,不過收益良多,不只是主席樹,還有其他之類的。
主席樹是什麼?想必你在看我部落格之前也看過不少大牛的部落格了,但是我提一下,主席樹就是用以解決類似於不斷的求區間內第幾(條件:大、小、自定義.....)的數或者狀態查詢。
那麼我開始建樹講解了:我想要建樹,想要做到新建的鏈能做到字首和的作用,那麼就要有儲存前乙個根節點的這樣一步,我們要取字首,就是要把這個根覆蓋之前的樹,然後呢,不是應該要插進去乙個數呢,如何插,要是這數大小不規定這樣的怎麼辦?——這就是為什麼我們要通過離散化來儲存全體元素了。
這樣匯出了離散化:假如我們有這樣的一串數:,我們若是直接處理,就很難確定左右兒子節點之間關係,我們不如離散化;對應我們對其一開始的輸入順序為,那麼為了離散化,我們總歸得排序,此時對應的輸入順序id變成了,但我們建樹得通過按輸入順序建樹,我們還得想個辦法返回回去,那麼豈不複雜,我列個表來簡化它:
初始輸入元素:3,5,4,9,10;
對應id:1,2,3,4,5;
排序後順序:3,4,5,9,10;
對應id:1,3,2,4,5;
我們想要知道原來的輸入順序,可以通過對應id來返回:
定乙個陣列lsan:將排序後的對應id一一存進去,
lsan[1]=1;
lsan[3]=2;
lsan[2]=3;
lsan[4]=4;
lsan[5]=5;
於是,我們平攤開來看:
i:1,2,3,4,5;
lsan[i]:1,3,2,4,5;這不就是初始輸入資料離散化後的相對值嘛。
好了,離散化的處理已經搞定了,我們來繼續處理更新建立新的鏈的做法:
一開始,我們有一棵空的樹,往後面建立鏈,我們讓初始的空樹的根節點定義為root[0],後面就是root[i],裡面放進了i個節點,然後將節點(輸入的元素點)放進去,我們有個記錄節點數的cnt,用來儲存此時已有節點數,然後我們反向更新放進函式時候的根節點的值,並且對於目前根節點內的數的個數我們「+1」,將輸入的點與(l+r)>>1(l、r為目前區間的兩極)比較大小,看它是放在左兒子還是右兒子,為什麼這樣比較大小可行?我們離散化過後,相當於是把每個節點用1~n來表示了,所以,現在放在左還是右,相當於是在用二分來儲存了,然後,我們向下更新子節點,直到葉子節點返回。
update(root[i], 1, n, lsan[i]);
void update(int &rt, int l, int r, int num) //建新的鏈,rt之所以這樣建為了通過下面來d反推rt的值
上面是建新鏈時候的**,rt是由往下之後反過來更新的,指的是目前節點的值,然後就是往下更新。
我們建立完樹之後就要來查詢值了例如【ql, qr】區間內的第k小的值,既然是字首和思想,我們看成【1,qr】的樹剪去【1,ql-1】的樹所剩下的第k大的值,畢竟大的樹是絕對包含前面的樹的元素的,前面的更新建鏈操作可以看出來,我們比較目前兩個節點的左兒子的差值,可以直到左節點的多出來的那部分的值的個數d,然後和我們所要查的k來比較如果相比d>=k,那麼就在左兒子裡面找就是了,不然去右兒子裡找k-d位的值,以此類推,推到最後假如只剩葉子,就返回那個葉子。
scanf("%d%d%d", &e1, &e2, &e3);
printf("%d\n", val[query(root[e1-1], root[e2], e3, 1, n)].x);
上面的輸入中是查詢的區間,和第e3小的值,因為root[i]存放的是有i個節點時候的根節點的值,所以相當於兩棵樹的差,大的樹的根是root[e2],被減樹的根是root[e1-1],val[i].x指的是第i個對應的已排序的點的值,而query()返回的又是對應的離散化後的值,就是排序後的順序,所以沒差,直接輸出是對的。
int query(int i, int j, int k, int l, int r) //在[i+1, j]區間內尋,不如就化成i[1, i]與[1, j]這樣的區間差
查詢的函式在上面給出。
最後,就是上完整**了。
#include #include #include #include #include #include #include #include #include #include #include #include #define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef long long ll;
const int maxn=100005;
int n, m, cnt;
struct node
}tree[maxn*20]; //別太小,「<<2」是線段樹,這裡可不是一棵線段樹,好多棵線段樹的
struct value
}val[maxn];
bool cmp(value e1, value e2)
int query(int i, int j, int k, int l, int r) //在[i+1, j]區間內尋,不如就化成i[1, i]與[1, j]這樣的區間差
int main()
sort(val+1, val+1+n, cmp); //開始離散化,之後所對應順序的.x就是其真實值
for(int i=1; i<=n; i++) //離散化之後,我們為了建樹將離散後陣列排序,離散後應當在的位置
init();
for(int i=1; i<=n; i++)
while(m--)
}return 0;
}
python 通熟易懂的閉包
usr bin python coding cp936 python ver2.7 閉包 closure 是函式式程式設計的重要的語法結構。函式式程式設計是一種程式設計正規化 而面向過程程式設計和物件導向程式設計也都是程式設計正規化 在面向過程程式設計中,我們見到過函式 function 在物件導向...
通熟易懂的正規表示式講解
以下分別舉例說明 the 開頭一定要有 the 字串 of despair 結尾一定要有 of despair 的字串 那麼,abc 就是要求以abc開頭和以abc結尾的字串,實際上是只有abc匹配 notice 匹配包含notice的字串 你可以看見如果你沒有用我們提到的兩個字元 最後乙個例子 就...
主席樹 靜態 的輕鬆入門
n i 1 85 kj 0i2 i 1n 58 j 0ki2 emmm 最近入門了主席樹,感覺其實不是很難,主要理解了就很簡單了 畢竟 這麼短 給出乙個數列,求區間 l r 之間的第 k 大值利用數列中 n 個資料建立 n 棵樹,其中第 i 棵樹維護 1 i 這個字首內的資料資訊 第 i 棵樹中的每...