ZJOI2013 K大數查詢

2021-10-01 06:14:22 字數 3626 閱讀 9841

點此看題

0x01 樹套樹

這道題的思路特別巧妙,樹套樹不一定要用區間線段樹套權值線段樹,還可以反過來套。

我們維護乙個動態開點的權值線段樹,每個點代表權值[l,

r]

[l,r]

[l,r

]在整個區間的出現情況,套上乙個動態開點的區間線段樹,操作1

11對權值線段樹單點修改,然後對每個點的[a,

b]

[a,b]

[a,b

]區間修改。

操作2

22先算右子樹根的[a,

b]

[a,b]

[a,b

]和,如果答案在裡面,找右子樹,否則減掉之後找左子樹,最後跑到葉子就得到了答案,時間複雜度o(n

log⁡2)

o(n\log^2)

o(nlog2)

,空間複雜度o(n

log⁡2)

o(n\log^2)

o(nlog2)

。這道題是需要卡常的,sum

sumsu

m需要開uns

igne

d_in

tunsigned\_int

unsign

ed_i

nt,輸入的c

cc要開lon

g_lo

ng

long\_long

long_l

ong。

#include

#define uint unsigned int

#define ll long long

const

int maxn =

500005

;int

read()

int n,m,a,b,rt,cnt1,cnt2;ll c;

struct node1

tr[maxn*40]

;struct node2

tr[maxn*20]

;void

modify

(int x,

int l,

int r,

int v)

voidup(

int x)

void

down

(int x,

int l,

int r)

void

add(

int&x,

int l,

int r,

int l,

int r)

down

(x,l,r)

;int mid=

(l+r)

>>1;

add(tr[x]

.ls,l,mid,l,r)

;add

(tr[x]

.rs,mid+

1,r,l,r);up

(x);

}uint query

(int x,

int l,

int r,

int l,

int r)

void

modify

(int

&x,int l,

int r,

int id)

intquery

(int

&x,int l,

int r,

int s)

intmain()

if(op==2)

}}

0x02 整體二分

首先我們把詢問和修改放在一起,然後在遞迴的時候維護(ql

,qr,

l,r)

(ql,qr,l,r)

(ql,qr

,l,r

),就表示[ql

,qr]

[ql,qr]

[ql,qr

]之間詢問的答案在[l,

r]

[l,r]

[l,r

]之中,每次拆分的時候考慮權值的中點mid

midmi

d,然後維護出乙個樹狀陣列(這裡的區間修改為加1

11,可以同時支援區間修改和區間查詢),修改的話考慮是否大於mid

midmi

d,如果大於mid

midmi

d就把這個修改歸類到右邊,同時修改樹狀陣列,否則歸類到左邊,不修改。

詢問的話就先算出這個區間中大於mid

midmi

d的個數,如果大於mid

midmi

d就分到左邊(因為答案在左邊,參考線段樹這類問題的求法),然後減去這個個數,否則分到右邊。最後我們把左右分別遞迴,知道權值區間只剩乙個值就確定了答案。

#include

#define int long long

const

int m =

50005

;int

read()

int n,m,c1[m]

,c2[m]

,pd[m]

,ans[m]

;struct node

p[m]

,l[m]

,r[m]

;int

lowbit

(int x)

void

upd(

int x,

int f)

intask

(int x)

void

cdq(

int ql,

int qr,

int l,

int r)

int mid=

(l+r)

>>

1,nl=

0,nr=0;

for(

int i=ql;i<=qr;i++

)else

}for

(int i=ql;i<=qr;i++)if

(p[i]

.op==

1&& p[i]

.v>mid)

upd(p[i]

.l,-1)

,upd

(p[i]

.r+1,1

);for(

int i=

1;i<=nl;i++

) p[ql+i-1]

=l[i]

;for

(int i=

1;i<=nr;i++

) p[ql+nl+i-1]

=r[i]

;cdq

(ql,ql+nl-

1,l,mid)

;cdq

(ql+nl,qr,mid+

1,r);}

signed

main()

;if(op==

2) pd[i]=1

;}cdq(

1,m,

1,n)

;for

(int i=

1;i<=m;i++)if

(pd[i]

)printf

("%d\n"

,ans[i]);

}

ZJOI2013 K大數查詢

有n個位置,m個操作。1 a b c形式,表示在第a個位置到第b個位置,每個位置加入乙個數c 2 a b c形式,表示詢問從第a個位置到第b個位置,第c大的數是多少。區間的第k大值有一種二分的做法。二分答案mid,計算出區間內 mid的值有多少個。若數量小於c,則ans mid,否則ans mid。...

ZJOI2013 K大數查詢

有n個位置,m個操作。操作有兩種,每次操作如果是 2 a b c 表示詢問從第a個位置到第b個位置,第c大的數是多少。輸入格式 第一行n,m接下來m行,每行形如1 a b c或2 a b c 輸出格式 輸出每個詢問的結果 solution 整體二分。假設我們現在要解決 ql,qr 並且他們的答案 加...

ZJOI2013 K大數查詢

顯然可以線段樹套線段樹。但是必須卡常。所以我採用了整體二分。注意是區間第k大,而不是第k小。然後區間轉化一下即可。有乙個剪枝的地方,就是如果這個區間已經沒有查詢的操作了,那就不用遞迴了。ac pragma gcc optimize ofast funroll all loops include de...