主席樹 2012集訓隊互測 Middle

2021-06-20 22:11:04 字數 1632 閱讀 4260

jzoj 2902 集訓隊互測2012 middle(陳立傑)

【題解】

這題雖然不是這幾天做的,但是最近在搞資料結構,再總結一下還是會有收穫的。

(複習時應該看看)

設乙個序列s從大到小排序後為s[1..k],則中位數為m=s[k/2向上取整]。

那就意味著,在序列s中,大於等於m的數大於等於k/2。如果把序列s轉化為t,t[i]=1(s[i]>=m),-1(s[i]

這樣我們很容易想到乙個演算法:二分答案+主席樹

1.先按位置建一棵葉子節點權值為1的原始線段樹(第乙個版本的線段樹)。要維護區間和sum,區間左側最大字首和lsum,和右側最大字首和rsum。

2.然後按權值從小到大的順序依次在相應的位置修改,每次新建乙個版本的線段樹(可持久化)

假設當前到第i個版本的線段樹,則要把第i-1大的數的位置權值修改為-1。這樣在第i個版本的線段樹上,比第i大的數還大(或等於)的數的位置上值為1,否則為-1。

3.二分答案。

二分到k時,就在第k個版本的線段樹上查詢。(程式實現時root[0]為第乙個版本線段樹的根)。

若query(root[k-1],1,n,ask[1],ask[2]).rsum + query(root[k-1],1,n,ask[2]+1,ask[3]-1).sum + query(root[k-1],1,n,ask[3],ask[4]).lsum >= 0,就說明k可以為中位數,繼續二分。

#include#include#include#include#include#define fo(i,a,b) for (int i = a;i <= b;i ++)

#define fd(i,a,b) for (int i = a;i >= b;i --)

using namespace std;

const int maxn = 2000000;

struct node

node(int a,int b,int c)

};int root[maxn];

node tree[maxn];

int a[maxn],b[maxn],pos[maxn];

int n,q,tot,n,last;

int ask[10];

bool cmp(int x,int y)

void readin()

void discretize()

void update(int z)

void addpoint(int &z,int x,int l,int r,int p)

else

else

update(z); }}

void filltree(int &z,int l,int r)

else }

void maketree()

void initialize()

node query(int z,int l,int r,int x,int y)

}bool check(int k)

void work()

last = a[pos[l]];

printf("%d\n",a[pos[l]]);

}int main(void)

return 0;

}

集訓隊互測 2012 Attack

description chnlich非常喜歡玩三國志這款遊戲,並喜歡用一些策略出奇制勝。現在,他要開始征服世界的旅途了。他的敵人有n nn座城市和n nn個太守,n nn個城市可以看作在二維平面上的n nn個點。n nn座城市的標號為0,1 2,n 1 0,1,2,cdots,n 1 0,1,2,...

2019集訓隊互測 學習軌跡(貪心)

首先沒有限制的話排個序就完事了。如果有限制的話,顯然我們需要幹的事情就是做出修正。按照權值從小到大排序。考慮什麼情況下非法,權值小的依賴權值大的。這個時候的修正非常明顯,把這個區間拎出來,無依賴的公升序,然後有依賴的降序排在後面。這樣的區間可以通過一次差分字首和找出來。容易注意到有的時候我們把若干個...

集訓隊互測2015 Robot

題目描述 題解 維護兩顆線段樹,維護最大值和最小值,因為每次只有單點查詢,所以可以直接在區間插入線段就可以了。注意卡常,不要寫stl,用鍊錶把同類修改串起來就好了。pragma gcc optimize 2 pragma gcc optimize 3 include include include ...