題目描述一些奇怪的東西給定乙個含有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.修改 在修改之前,我們應先把序列裡原來的值在...