樹狀陣列套主席樹 帶修改區間K大數

2022-05-30 17:30:11 字數 3294 閱讀 4679

題目描述

給定乙個含有n個數的序列a[1],a[2],a[3]……a[n],程式必須回答這樣的詢問:對於給定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的數是多少(1≤k≤j-i+1),並且,你可以改變一些a[i]的值,改變後,程式還能針對改變後的a繼續回答上面的問題。你需要編乙個這樣的程式,從輸入檔案中讀入序列a,然後讀入一系列的指令,包括詢問指令和修改指令。

對於每乙個詢問指令,你必須輸出正確的回答。

輸入輸出格式

輸入格式:

第一行有兩個正整數n(1≤n≤100000),m(1≤m≤100000)。分別表示序列的長度和指令的個數。

第二行有n個數,表示a[1],a[2]……a[n],這些數都小於10^9。接下來的m行描述每條指令,每行的格式是下面兩種格式中的一種。 q i j k 或者 c i t

q i j k (i,j,k是數字,1≤i≤j≤n, 1≤k≤j-i+1)表示詢問指令,詢問a[i],a[i+1]……a[j]中第k小的數。

c i t (1≤i≤n,0≤t≤10^9)表示把a[i]改變成為t。

輸出格式:

對於每一次詢問,你都需要輸出他的答案,每乙個輸出佔單獨的一行。

輸入輸出樣例

輸入樣例#1: 

5 33 2 1 4 7

q 1 4 3

c 2 6

q 2 5 3

輸出樣例#1: 36

說明10%的資料中,m,n≤100;

20%的資料中,m,n≤1000;

50%的資料中,m,n≤10000。

對於所有資料,m,n≤100000

請注意常數優化,但寫法正常的整體二分和樹套樹都可以以大約1000ms每個點的時間過。

本題資料為洛谷自造資料,使用cyaron耗時5分鐘完成資料製作。

一些奇怪的東西

這個大概是luogu某題目,然而我太菜了,所以現在才會做。

這個題目顯然是由一些靜態的東西衍生而來的(沒錯就是靜態區間k大數)

hmm...昨天(可能是今天??)有人問我,一些靜態和動態的問題,我列舉一下:

靜態區間k大數(排個序就好了)

靜態區間k大數(主席樹搞一搞)

待修改區間k大數(我就要講的這個嘛)

動態區間和(zkw線段樹/樸素線段樹/2個樹狀陣列)

靜態區間和(...字首和?)

步入正題

話說帶修改區間k大數(注意是修改而並不是插入)

顯然我們如果按照靜態區間k大數的方法搞,暴力更新整棵線段樹那麼複雜度將是o(n log2 n)修改每次

想到單點修改求字首和想到樹狀陣列。不妨用樹狀陣列維護線段樹每個節點的字首和,

換句話說要想求得每個節點具體的值,那麼必須將屬於這個節點的log2 n個節點的和全部累加,才是這個節點值域範圍內,字首插入數的個數

所以,樹狀陣列是維護乙個陣列表示的是要想知道當前每一節點值域是[l,r]字首插入數的個數,是哪log2 n個節點的累加和。

這樣子複雜度是o(log2

2n)每次插入。

查詢的時候也是這樣用r的字首插入數的個數減去(l-1)字首插入數的個數,就是該節點值域在[l,r]區間內插入數的個數。

這樣的複雜度也是o(log2

2n)每次查詢。

注意一些細節:

記錄那幾個點的陣列(node_add和node_cut只需開log2n個即可),

然後離散化的時候盡量不用vector(不好習慣)

對tmp離散化,t記錄離散化後下標

sort(tmp+1,tmp+1+tmp[0

]); t=unique(tmp+1,tmp+1+tmp[0])-tmp-1;

若要查詢某個數val離散化以後是多少,那麼就是

w=lower_bound(tmp+1,tmp+1+t,val)-tmp;

若要查詢某個離散化後的數kkk實際上是多少那麼直接訪問下標

w=tmp[kkk];

還是得解釋**:

# include # include 

# include

# include

# include

using

namespace

std;

const

int n=1e5+10

;# define lowbit(x) (x&(-x))

# define lson t[rt].ls,l,mid

# define rson t[rt].rs,mid+1

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

)int tmp[n<<1

];struct

recqes[n];

struct

seqment_treet[n*400];//

樹套樹空間得開 n log n

int node_cut[25],node_add[25]; //

這裡只要log n個就行後面memset會慢

intcnt_cut,cnt_add,tot;

introot[n],a[n];

intt,n,m;

inline

intread()

while(c>='

0'&&c<='

9') x=(x<<3)+(x<<1)+(c^48),c=getchar();

return w?-x:x;

}void write(int

x)//

i/o優化

void update(int &rt,int l,int r,int pos,int

val)

//普通主席樹的更改維護,值域+1/-1

void pre_update(int x,int

val)

//首先處理出那幾棵線段樹管這個陣列的位置的字首和的,然後每個線段樹分別維護

int query(int l,int r,int

k) else

}int pre_query(int l,int r,int

k)int

main()

sort(tmp+1,tmp+1+tmp[0

]); t=unique(tmp+1,tmp+1+tmp[0])-tmp;

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

);

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

else

}return0;

}

帶修改的區間第k小 樹狀陣列套主席樹

題目鏈結 對乙個序列進行兩種操作 對於不待修改的區間第k kk小,我們可以用主席樹完成。我們來看看只用主席樹如何完成帶修改的區間第k kk小。對於每次修改,我們都需要把當前位置及以後的主席樹都進行修改。因此每次修改的時間複雜度為o n log n o nlog n o nlog n 空間增加o n ...

帶修改區間k大

看了好久,後來自己想了一下。大概就是說因為原來的寫法下要修改的代價太大了,所以套上乙個樹狀陣列使每顆主席樹存特定幾個位置上的權值。平均一下修改和詢問的代價。include include include define n 2200001 using namespace std int n,m,tot...

Data 帶修改的主席樹 樹狀陣列套主席樹

樹狀陣列套主席樹 樹狀陣列的每個節點維護的是一段區間,我們將每個區間構造成一棵線段樹,這時候如果我們要修改乙個值,只需要修改logn個節點即可,時間複雜度為log 2 n 樹狀陣列維護的區間是數的個數n 離散化時是把所有數 包括要修改的數 全部離散化 1.修改 在修改之前,我們應先把序列裡原來的值在...