國家集訓隊 middle

2022-05-25 12:24:10 字數 1447 閱讀 3706

首先,看到中位數就應該會想到乙個經典做法,二分乙個\(mid\),把小於\(mid\)的數值為\(-1\),大於\(mid\)的數值為\(1\),如果這個區間的和\(>=0\)的話中位數可以更大,否則要更小

而題目中要求中位數最大,所以自然這個區間的和要越大越好,自然要多取\(1\)

我們知道\([b,c]\)這個區間是肯定會取到的,所以只要求和,而\([a,b-1],[c+1,d]\)這兩個區間乙個取最大字尾,乙個取最大字首,這樣就保證了中位數最大

但是每二分乙個\(mid\)就要重新把整個序列用\(-1,1\)代替,複雜度肯定承受不起,那怎麼辦呢,我們可以預處理每乙個數做\(mid\)時,每乙個區間最大字首,最大字尾,區間和,用線段樹來實現

但是對於每乙個數開一棵線段樹也是承受不起的,但是我們考慮到,如果我們按順序來列舉\(mid\)的時候(先排序離散),\(mid\)這個位置的線段樹,與\(mid+1\)這個位置的線段樹唯一的區別,就是離散後在\(mid\)這個位置的數由\(-1\)變為了\(1\),也就是說相鄰兩棵線段樹之間可以共用很多部分

立刻想到主席樹!

至於字尾字首爛大街的操作就不講了

下面是美滋滋的**時間~~~

#include#include#include#include#include#define n 20007

using namespace std;

struct tree

tr[n<<7];

int n,m,q,len,last,cnt;

int val1[n],val2[n],rt[n],qq[4];

vectorpos[n];

void pushup(int x)

void update(int &x,int l,int r,int p,int v)

if(pr)

return;

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

update(tr[x].ls,l,mid,p,v);

update(tr[x].rs,mid+1,r,p,v);

pushup(x);

//printf("ls:%d rs:%d\n",tr[x].sum,tr[x].lsum);

}int ask_sum(int x,int l,int r,int ll,int rr)

int ask_lsum(int x,int l,int r,int ll,int rr)

int ask_rsum(int x,int l,int r,int ll,int rr)

int main()

for(int i=1;i<=n;++i)

update(rt[len+1],1,n,i,-1);

for(int i=len;i;--i)

last=val2[l];

printf("%d\n",last);

} return 0;

}

國家集訓隊 middle

傳送門 按照中位數題的套路,二分答案 mid 序列中 ge mid 記為 1 mid 的記為 1 然後只要存在乙個區間 l,r l in a,b r in c,d 的和 ge 0 則答案可以更大,否則就更小。所以說我們就要算出區間 b 1,c 1 的和,加上 a,b 的最大字尾,還有 c,d 最大字...

國家集訓隊 middle

考慮二分答案,把問題轉化成可行性問題,然後發現b 1 c 1 是一定必選的,另外兩邊要想最大就是維護乙個連續最大子段和 然後每次二分值改變,只會影響線段樹上log個位置,於是考慮主席樹 includeusing namespace std inline intread while ch 0 ch 9...

國家集訓隊 middle

乙個長度為n的序列a,設其排過序之後為b,其中位數定義為b n 2 其中a,b從0開始標號,除法取下整。給你乙個長度為n的序列s。回答q個這樣的詢問 s的左端點在 a,b 之間,右端點在 c,d 之間的子串行中,最大的中位數。其中a位置也從0開始標號。第一行序列長度n。接下來n行按順序給出a中的數。...