Luogu 2839 國家集訓隊 middle

2022-06-13 04:15:11 字數 3112 閱讀 1212

感覺這題挺好的。

首先對於中位數最大有乙個很經典的處理方法就是二分,每次二分乙個陣列中的下標$mid$,然後我們把$mid$代回到原來的陣列中檢查,如果乙個數$a_ \geq mid$,那麼就把$s_$記為$1$,否則把$s_$記為$-1$,然後對$s_$跑一遍字首和,觀察是否有乙個區間的和不小於$0$。

讀清楚題意之後發現在這題中,如果要對乙個長度為偶數(記為$n$)的序列求中位數,那麼答案為排好序的陣列中下標為$n / 2 + 1$的元素。(下標從$1$開始),不同的中位數$s_$的表示方法可能有少許不同,要具體題目具體yy。

證明應當也很簡單,$+1$代表乙個比當前的$mid$大的數,而$-1$表示乙個比當前的$a_$小的數,那麼當$a_$是中位數的條件在乙個區間成立的時候,這個區間的$s_$和應當恰好等於$0$。而當得到了乙個區間和大於$0$的區間的時候,我們可以通過扔掉幾個$1$使它變成$0$,相當於之前的條件成立。

但是這樣還是太慢了。

對於本題來說,每次二分得到了乙個$mid$,我們檢驗的時候得到的答案就一定是(區間是$a, b, c, d$)$sum(b + 1, c - 1) + lmax(c, d) + rmax(a, b)$。

其中$lmax$代表一定要選左端點的最大子段和,而$rmax$代表一定要選右端點的最大子段和。

顯然可以用線段樹來維護。

乙個元素開一顆線段樹是不可能的……

我們發現在排好序中的陣列裡面兩個下標相差$1$的元素的值其實只有$1$個不同,這就是較小的元素在原陣列中的下標,那麼大多數結點的值可以繼承過來。

於是可以可持久化了,變成了乙個主席樹。

時間複雜度$o(nlog^n)$。

code:

#include #include 

#include

using

namespace

std;

const

int n = 2e4 + 5

;const

int inf = 1

<< 30

;int

n, qn, a[n];

struct

innum b[n];

bool cmp(const innum &x, const innum &y)

/*bool cmp(const innum &x, const innum &y)

*/inline

void read(int &x)

inline

int max(int x, int

y) inline

void chkmax(int &x, int

y) namespace

psegt

} s[n * 20

];

int root[n], nodecnt = 0

;

#define lc(p) s[p].lc

#define rc(p) s[p].rc

#define sum(p) s[p].sum

#define lmax(p) s[p].lmax

#define rmax(p) s[p].rmax

#define mid ((l + r) >> 1)inline

void up(int

p)

void build(int &p, int l, int

r)

build(lc(p), l, mid);

build(rc(p), mid + 1

, r);

up(p);

}void modify(int &p, int l, int r, int x, int v, int

pre)

if(x <=mid) modify(lc(p), l, mid, x, v, lc(pre));

else modify(rc(p), mid + 1

, r, x, v, rc(pre));

up(p);

}node query(

int p, int l, int r, int x, int

y)

#undef lc

#undef rc

#undef sum

#undef lmax

#undef rmax

#undef mid }

using

namespace

psegt;

inline

bool chk(int mid, int l1, int r1, int l2, int

r2)

now.init();

now = query(root[mid], 1

, n, l1, r1);

res +=now.rmax;

now.init();

now = query(root[mid], 1

, n, l2, r2);

res +=now.lmax;

return res >= 0;}

inline

int solve(int l1, int r1, int l2, int

r2)

return

b[res].val;

}int

main()

s[0].lmax = s[0].rmax = -inf;

build(root[

1], 1

, n);

sort(b + 1, b + 1 +n, cmp);

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

modify(root[i],

1, n, b[i - 1].id, -1, root[i - 1

]);

read(qn);

for(int ans = 0, q[4]; qn--; )

sort(q, q + 4

); ans = solve(q[0], q[1], q[2], q[3

]); printf(

"%d\n

", ans);

}

return0;

}

view code

Luogu P2839 國家集訓隊 middle

首先 b,c 是必選的,然後選一段 a,b 的字尾和一段 c,d 的字首 都可空 對於中位數 這裡中位數採用這道題的定義 有個常見的處理方式 二分 mid,將 0,則說明 mid 的佔到了一半以上,即中位數 mid。採用這種處理方式,二分中位數,由於要中位數盡量大,所以要貪心,選字尾和字首使得大於等...

P2839 國家集訓隊 middle

提一下靜態區間第k小的nlog2n的做法 1.建關於排名的主席樹 按排名順序建樹 2.二分答案。這樣做靜態區間第k小的雖然有些zz,但它的意義在於將線段樹 維護的物件改變了。1 include2 using namespace std 3int n,m,cnt 4int a 5 midd 5int ...

Luogu1501 國家集訓隊 Tree II

題目描述 一棵n個點的樹,每個點的初始權值為1。對於這棵樹有q個操作,每個操作為以下四種操作之一 u v c 將u到v的路徑上的點的權值都加上自然數c u1 v1 u2 v2 將樹中原有的邊 u1,v1 刪除,加入一條新邊 u2,v2 保證操作完之後仍然是一棵樹 u v c 將u到v的路徑上的點的權...