淺談第 k 大


由於我比較菜,所以有什么錯誤請盡管提出,感謝!(大小挺多反的)

什么是第k大?

就是把一段序列按從小到大排序,下標規定從1開始,下標為k的數即為此段序列的第k大。

如何解決?

下文中將使用長度為 \(n\)\(a\) 序列,數列最大值為 \(V\) ,並用 \(O()-O()-O()\) 表示預處理時間復雜度,單次查詢時間復雜度,空間復雜度。

1.一個 \(naive\) 的想法,使用 \(sort\) 使數組有序,然后輸出下標為 \(k\) 的數。顯然,這樣做的復雜度是 \(O(1)-O(n \log n)-O(n)\) 的。

2.我們發現,我們並不需要整個數組有序,只找出第k大就好了,那么可以利用快速排序分治的思想,每次遞歸中隨機選取一個數記為 \(pos\) ,將序列分為 \(3\) 段,即 \([l,pos-1],[pos,pos],[pos+1,r]\) 使得第一段的數均小於 \(a[pos]\) ,第三段的數均大於 \(a[pos]\) ,判斷下 \(pos\) 是否等於 \(k\) 即可。

  • 復雜度為 \(O(1)-O(n)-O(n).\)

  • 值得注意的是,\(STL\)\(nth \_element\) 可以直接使用。

  • 代碼(引用自oi-wiki)

// 模板的T參數表示元素的類型,此類型需要定義小於(<)運算
template <typename T>
// arr為查找范圍數組,rk為需要查找的排名(從0開始),len為數組長度
T find_kth_element(T arr[], int rk, const int len) {
  if (len <= 1) return arr[0];
  // 隨機選擇基准(pivot)
  const T pivot = arr[rand() % len];
  // i:當前操作的元素
  // j:第一個等於pivot的元素
  // k:第一個大於pivot的元素
  int i = 0, j = 0, k = len;
  // 完成一趟三路快排,將序列分為:小於pivot的元素 | 等於pivot的元素 |
  // 大於pivot的元素
  while (i < k) {
    if (arr[i] < pivot)
      swap(arr[i++], arr[j++]);
    else if (pivot < arr[i])
      swap(arr[i], arr[--k]);
    else
      i++;
  }
  // 根據要找的排名與兩條分界線的位置,去不同的區間遞歸查找第k大的數
  // 如果小於pivot的元素個數比k多,則第k大的元素一定是一個小於pivot的元素
  if (rk < j) return find_kth_element(arr, rk, j);
  // 否則,如果小於pivot和等於pivot的元素加起來也沒有k多,則第k大的元素一定是一個大於pivot的元素
  else if (rk >= k)
    return find_kth_element(arr + k, rk - k, len - k);
  // 否則,pivot就是第k大的元素
  return pivot;
}

3.我們又有一個 \(navie\) 的想法,我們可以開個桶去記錄啊!然后暴力枚舉 \([1,V]\) 的數,開個 \(res\) 累計下數量是否到 \(k\) 即可。顯然,這樣復雜度是 \(O(n)-O(V)-O(V)\) 的。

4.我們發現,我們桶的思想就是枚舉到 \(i\) 時,知道了小於等於 \(i\) 的數的數量,並判斷這個數量是否大於等於k。

  • 於是,我們得到,假如一個數是第 \(k\) 大,那么一定至少有 \(k\) 個數小於等於它。那么我們只需要找出第一個滿足這個條件的數即可。

  • 我們發現這滿足單調性,因為假如 \(2\)\(i,j,(i<j)\),滿足有 \(k\) 個數小於等於 \(i\) ,那么必定有 \(k\) 個數小於等於 \(j\)

  • 對於快速求出小於等於某一個數的數量,我們可以使用值域樹狀數組,對於尋找這個數,我們可以二分。

  • 復雜度為 \(O(n \log V)-O(log^2V)-O(V)\)

  • 對於值域過大的時候可以考慮動態開點樹狀數組,開個 \(map\) 記錄需要用到的 \(id\) 即可。

5.對頂堆

  • 我們分別開一個大根堆和一個小根堆,那么有個很好的性質:假如大根堆的堆頂小於小根堆堆頂,那么大根堆中的所有元素都小於小根堆中的所有元素。

  • 這啟發我們可以限制大根堆的 \(size=k\)

  • 那么我們可以先往大根堆 \(push\)\(k\) 個元素,之后對於一個元素,假如大於大根堆堆頂,就進去小根堆,否則大根堆彈出堆頂,然后再進去大根堆,可以知道第k大即為最后大根堆堆頂。

  • 不過這個東西的局限性比較大,只能適用於前綴區間的第k大,所以復雜度只討論單次的查詢,即為 \(O(\log n)\).

6.權值線段樹

  • 既然有了權值樹狀數組,那么肯定也有權值線段樹。

  • 我們對於一個數 \(a[i]\) ,將線段樹的 \([a[i],a[i]]+1\) ,當查詢第k大時,因為 左子樹所有的值都小於右子樹 所以我們可以判斷左子樹的 \(size\) 是否大於k,是遞歸左子樹;不是,將 \(k-=size[left \_son]\) 之后遞歸右子樹。 直至遞歸到葉子節點時,答案即為當前值。

  • 注意特判下 \(k > size[root]\) 的情況。

  • 復雜度 \(O(n \log V)-O(\log V)-O(4V)\),這里認為先 \(build\) 整個序列,但需要注意的是,單靠權值線段樹無法做到區間第 \(k\) 大。

7.平衡樹

  • 詳見模板題

  • 復雜度 \(O(n \log n)-O(\log n)-O(n)\),使用平衡樹時需要考慮常數帶來的影響。

  • 這里推薦一種比較好寫的平衡樹,可以用 \(01-trie\) 去代替,因為 \(01-trie\) 有一個性質,即左子樹的點都比右子樹小,所以可以仿照權值線段樹的查詢方式,對於區間第 \(k\) 大上可持久化即可,代碼編寫難度不高。

8.序列分塊套二分(支持區間第 \(k\) 大)

  • 我們可以塊內維護有序序列,然后對於查詢 \([l,r]\) 中有多少個數小於等於 \(x\) 時,我們可以散塊暴力,整塊 \(upper\_bound\) ,並記錄塊內 \(max\)\(min\) 減小常數即可。

  • 顯然復雜度為 \(O(n \sqrt{n \log n})-O(\log V \sqrt{n \log n}) - O(n).\)

  • 模板.

9.值域分塊套序列分塊(支持區間第 \(k\) 大)

  • 對於分塊來說,應該能省一個 \(log\) 就省掉,在 \(V\)\(n\) 同階時,我們可以考慮以下做法。

  • 維護 \(cnt1[i][j]\) 表示第 \(i\) 塊值域在第 \(j\) 塊的個數前綴和, \(cnt2[i][j]\) 維護第 \(j\) 塊值為 \(j\) 的個數前綴和。

  • 對於查找,我們應該先統計出散塊的貢獻,然后先確定答案所在值域的塊,之后再在值域塊內查找。

  • 這樣子復雜度就做到了 $O(V\sqrt{n})-O(\sqrt{V})-O(V\sqrt{n}). $

  • 模板

  • 但對於這題的修改操作,我們還需要開一個並查集,對於相同值的指向同一個根並記錄每個點的根,然后修改時更改這個根的值即可。 \(push\_down\) 時將每個點的值設為並查集所指向的根節點的權值。

  • 值得注意的是,這個東西可以再用塊狀鏈表每個塊的位置關系,然后暴力找出操作左右塊,其他類似於上述的即可維護,做到可插入

10.主席樹(靜態區間第 \(k\) 大)

  • 顯然,權值線段樹缺點就是無法知道區間的權值線段樹長什么樣。對此,我們可以利用前綴和。即 \([l,r]\) 的權值線段樹等於 \([1,r]\) 的減去 \([1,l-1]\) 的。

  • 如何維護前綴權值線段樹?假設黑色點為原樹,紅色點為新樹,\(new\) 點為改變的葉子結點。我們可以發現,新樹在原樹上就改變了一個點,那么對於沒有改變的左右子樹,我們可以直接從原樹轉移,對於改變了,開新點然后權值加上即可,這里注意要動態開點才能保證空間復雜度正確。

  • 模板

  • 復雜度 \(O(n \log V)-O(log V)-O(n \log n)\)

11.樹套樹(動態區間第k大)

  • 我們發現,修改后我們需要快速維護前綴和,假如暴力維護的話復雜度單次會 \(O(n)\),於是我們上個樹狀數組,每次修改 \(\log n\) 棵權值線段樹,查詢時也查詢 \(\log n\) 棵權值線段樹,得到 \([l,r]\)\(size\).

  • 模板.

  • 復雜度 \(O(n \log n \log V)-O(\log n \log V)-O(n \log n \log V).\)

  • 其實不止樹狀數組套線段樹,還有線段數套線段樹,線段樹套平衡樹。線段數套平衡樹.

12.整體二分(離線,動態區間第k大)

  • 還是回到4,我們發現我們要是能把所有操作都整合在一起就好了。

  • 我們先考慮不帶修。

  • 假如我們現在值域遍歷到 \([l,r]\) ,答案在這個區間的詢問區間為 \([L,R]\),那么我們可以設定 \(mid\),對於權值小於等於 \(mid\) 的先全部加入,再對於每一個詢問判斷答案是否在 \([1,mid]\) 之中還是 \([mid+1,r]\) 之中,這個可以通過樹狀數組來查詢值域在 \([1,mid]\) 中且序列在 \([query\_l,query\_r]\) 之中的個數(因為在樹狀數組里面的都是值域 \([1,mid]\) 的,所以只要查在查詢區間里的即可)。

  • 對於詢問可以划分為答案在值域在左子樹的和右子樹的,遞歸到葉子即為所有詢問的答案。

  • 考慮帶修,我們對於每一個點記錄 \(pre[i]\) 表示之前存的值,令 \((x,w)\) 為對答案貢獻 \(w*x\),添加操作 \((pre[i],-1)\)\((new,1)\) 即可。

  • mcyl35大佬的優化

13.莫隊維護區間樹狀數組做到區間第k大 (離線)

  • 字面意思,處理好區間值域樹狀數組的時候二分答案即可。

  • 我們用離散化就能輕松做到\(O(m \log m)-O(\sqrt{n}\log n)-O(n)\)

  • 好處也可能就是比較好寫且空間較小,代碼在后面會有,竟然能跑過主席樹的模板?!


接下來是一些奇怪的第 \(k\) 大。

1.矩陣第 \(k\)

  • 我們是可以先對整個矩陣的值從小到大排序,之后整體二分套二維樹狀數組查詢即可。

2.樹上路徑第 \(k\)

  • 還是整體二分,對於查詢多少個數在這個序列范圍上樹剖大力維護每條重鏈上的貢獻即可。

Code:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <vector>

#define N (int)(1e5+5)
#define SN (int)(1e3+5)
#define il inline
#define fp(i,qaq,qwq) for(int i=(qaq);i<=(qwq);++i)
#define fd(i,qaq,qwq) for(int i=(qaq);i>=(qwq);--i)
#define id(xgf) ((xgf-1)/blk+1)

using namespace std;

int b[SN][SN],cnt[SN],tag[SN],a[N],gr[SN][2];
int n,m,blk;

il int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)) {
		sum=(sum<<3)+(sum<<1)+ch-'0';
		ch=getchar();
	}
	return sum*f;
}

il void pr(int x) {
	if(x<0) {
		putchar('-');
		x=-x;
	}
	if(x>9) pr(x/10);
	putchar(x%10+'0');
}

il void reset(int x) {
	cnt[x]=0;
	fp(i,gr[x][0],gr[x][1]) b[x][cnt[x]++]=a[i];
	sort(b[x],b[x]+cnt[x]);
}

il void update(int x,int y,int z) {
	if(id(x)==id(y)) {
		fp(i,x,y) a[i]+=z;
		reset(id(x));
	} else {
		fp(i,x,gr[id(x)][1]) a[i]+=z;
		reset(id(x));
		fp(i,id(x)+1,id(y)-1) tag[i]+=z;
		fp(i,gr[id(y)][0],y) a[i]+=z;
		reset(id(y));
	}
}

il int get_ans(int x,int y,int z) {
	if(id(x)==id(y)) {
		int tot=0;
		fp(i,x,y) if(a[i]+tag[id(x)]<=z) ++tot;
		return tot;
	} else {
		int tot=0;
		fp(i,x,gr[id(x)][1]) if(a[i]+tag[id(x)]<=z) ++tot;
		fp(i,gr[id(y)][0],y) if(a[i]+tag[id(y)]<=z) ++tot;
		fp(i,id(x)+1,id(y)-1) tot+=(b[i][cnt[i]-1]+tag[i])<=z?cnt[i]:(b[i][0]+tag[i]>z)?0:upper_bound(b[i],b[i]+cnt[i],z-tag[i])-b[i];
		return tot;
	}
}

il int qry(int x,int y,int z) {
	int answ=-1,l=-2e9,r=(int)(2e9);
	while(l<=r) {
		int mid=(l+r)>>1;
		if(get_ans(x,y,mid)>=z) answ=mid,r=mid-1;
		else l=mid+1;
	}
	return answ;
}

signed main() {
	n=rd(); m=rd(); blk=pow(n*log(n),0.425);
	int op,x,y,z,tmp=-1;
	fp(i,1,n) {
		a[i]=rd();
		if(id(i)!=tmp) gr[id(i)][0]=i,tmp=id(i);
		else gr[id(i)][1]=i;
		b[id(i)][cnt[id(i)]++]=a[i];
	}
	fp(i,1,id(n)) sort(b[i],b[i]+cnt[i]);
	fp(i,1,m) {
		op=rd(); x=rd(); y=rd(); z=rd();
		if(op==1) {
			pr(qry(x,y,z));
			puts("");
		} else {
			update(x,y,z);
		}
	}
	return 0;
}
#include <bits/stdc++.h>

#define N (int)(1e5+20)
#define M 320

using namespace std;

int n,m,sz,bl[N],a[N],L[M],R[M],cnt1[M][M],cnt2[M][N],sum1[M],sum2[N],id[M][N],rid[M][N],pos[N];

int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}

void pr(int x) {
	if(x<0) {putchar('-');x=-x;}
	if(x>9) pr(x/10);
	putchar(x%10+'0');
}

void build(int x) {
	int tot=0;
	for(int i=1;i<=sz;i++) id[x][rid[x][i]]=0;
	for(int i=L[x];i<=R[x];i++) {
		if(!id[x][a[i]]) id[x][a[i]]=++tot,rid[x][tot]=a[i];
		pos[i]=id[x][a[i]];
	}
}

void push_down(int x) {
	for(int i=L[x];i<=R[x];i++) a[i]=rid[x][pos[i]]; 
}

void solve(int l,int r,int x,int y) {
	for(int i=l;i<=r;i++) {
		if(a[i]==x) {
			--cnt1[bl[l]][bl[x]]; --cnt2[bl[l]][x];
			++cnt1[bl[l]][bl[y]]; ++cnt2[bl[l]][y];
			a[i]=y;
		}
	}
}

void update(int l,int r,int x,int y) {
	if(x==y||cnt2[bl[r]][x]-cnt2[bl[l]-1][x]==0) return;
	for(int i=bl[n];i>=bl[l];i--) cnt2[i][x]-=cnt2[i-1][x],cnt2[i][y]-=cnt2[i-1][y],cnt1[i][bl[x]]-=cnt1[i-1][bl[x]],cnt1[i][bl[y]]-=cnt1[i-1][bl[y]];
	if(bl[l]==bl[r]) {
		push_down(bl[l]);
		solve(l,r,x,y);
		build(bl[l]);
	} else {
		push_down(bl[l]); push_down(bl[r]);
		solve(l,R[bl[l]],x,y); solve(L[bl[r]],r,x,y);
		build(bl[l]); build(bl[r]);
		for(int i=bl[l]+1;i<bl[r];i++) {
			if(!cnt2[i][x]) continue;
			if(cnt2[i][y]) {
				push_down(i);
				solve(L[i],R[i],x,y);
				build(i);
			} else {
				cnt1[i][bl[y]]+=cnt2[i][x]; cnt1[i][bl[x]]-=cnt2[i][x];
				cnt2[i][y]=cnt2[i][x]; cnt2[i][x]=0;
				id[i][y]=id[i][x]; rid[i][id[i][x]]=y; id[i][x]=0;
			}
		}
	}
	for(int i=bl[l];i<=bl[n];i++) cnt2[i][x]+=cnt2[i-1][x],cnt2[i][y]+=cnt2[i-1][y],cnt1[i][bl[x]]+=cnt1[i-1][bl[x]],cnt1[i][bl[y]]+=cnt1[i-1][bl[y]];
}

int kth(int l,int r,int k) {
	int sum=0;
	if(bl[l]==bl[r]) {
		push_down(bl[l]);
		for(int i=l;i<=r;i++) sum2[i]=a[i];
		nth_element(sum2+l,sum2+l+k-1,sum2+r+1); int answ=sum2[l+k-1];
		for(int i=l;i<=r;i++) sum2[i]=0;
		return answ;
	}
	push_down(bl[l]); push_down(bl[r]);
	for(int i=l;i<=R[bl[l]];i++) ++sum1[bl[a[i]]],++sum2[a[i]];
	for(int i=L[bl[r]];i<=r;i++) ++sum1[bl[a[i]]],++sum2[a[i]];
	for(int i=1;i<=(N-1)/sz+1;++i) {
		if(sum+sum1[i]+cnt1[bl[r]-1][i]-cnt1[bl[l]][i]>=k){
			for(int j=(i-1)*sz+1;j<=i*sz;++j)
				if(sum+sum2[j]+cnt2[bl[r]-1][j]-cnt2[bl[l]][j]>=k){
					for(int o=l;o<=R[bl[l]];++o) --sum1[bl[a[o]]],--sum2[a[o]];
					for(int o=L[bl[r]];o<=r;++o) --sum1[bl[a[o]]],--sum2[a[o]]=0;
					return j;
				}
				else sum+=sum2[j]+cnt2[bl[r]-1][j]-cnt2[bl[l]][j];
		}
		else sum+=sum1[i]+cnt1[bl[r]-1][i]-cnt1[bl[l]][i];
	}
}

int main() {
	n=rd(); m=rd(); sz=sqrt(n);
	for(int i=1;i<N;i++) bl[i]=(i-1)/sz+1;
	for(int i=1;i<=n;i++) a[i]=rd();
	for(int i=1;i<=bl[n];i++) L[i]=(i-1)*sz+1,R[i]=i*sz; R[bl[n]]=n;
	for(int i=1;i<=bl[n];i++) build(i);
	for(int x=1;x<=bl[n];x++) {
		for(int i=1;i<=bl[N-1];i++) cnt1[x][i]=cnt1[x-1][i];
		for(int i=1;i<N;i++) cnt2[x][i]=cnt2[x-1][i];
		for(int i=L[x];i<=R[x];i++) ++cnt1[x][bl[a[i]]],++cnt2[x][a[i]];
	}
	int op,l,r,x,y;
	while(m--) {
		op=rd(); l=rd(); r=rd(); x=rd();
		if(op==1) y=rd(),update(l,r,x,y);
		else pr(kth(l,r,x)),puts("");
	}
	return 0;
}
#include <bits/stdc++.h>

#define N (int)(7e4+5)
#define M 1000
#define ED puts("")
#define il inline
il int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}

il void pr(int x) {
	if(x<0) {putchar('-');x=-x;}
	if(x>9) pr(x/10);
	putchar(x%10+'0');
}

char nex() {
	char ch=getchar();
	while(!isalpha(ch)) ch=getchar();
	return ch;
}

using namespace std;

struct node {
	int l,r,sz,cnt1[M],cnt2[N],a[M];
	int& operator [](const int k) {
		return a[k];
	}
}g[M];

int id[N],sum1[M],sum2[N];
int n,m,bl,tot;

il int kth(int l,int r,int k) {
	int lb,rb,no=1,qwq=1,res=0;
	while(g[no].sz<l) l-=g[no].sz,no=g[no].r;
	lb=no; no=1;
	while(g[no].sz<r) r-=g[no].sz,no=g[no].r;
	rb=no;
	//cout<<l<<" "<<r<<" "<<k<<" "<<lb<<" this is question "<<rb<<endl;
	if(lb==rb) {
		for(int i=l;i<=r;i++) {
			////cout<<val[lb][i]<<" ";
			++sum1[id[g[lb][i]]],++sum2[g[lb][i]];
		}
		qwq=1;
		while(sum1[qwq]<k) k-=sum1[qwq],++qwq;
		qwq=(qwq-1)*bl; //cout<<qwq<<endl;
		while(sum2[qwq]<k) k-=sum2[qwq],++qwq; 
		for(int i=l;i<=r;i++) sum1[id[g[lb][i]]]=0,sum2[g[lb][i]]=0;
		return qwq;
	}
	qwq=1;
	for(int i=l;i<=g[lb].sz;i++) {
		++sum1[id[g[lb][i]]],++sum2[g[lb][i]];
	}
	for(int i=1;i<=r;i++) ++sum1[id[g[rb][i]]],++sum2[g[rb][i]];
	while(sum1[qwq]+g[g[rb].l].cnt1[qwq]-g[lb].cnt1[qwq]<k) {
		k-=sum1[qwq]+g[g[rb].l].cnt1[qwq]-g[lb].cnt1[qwq]; ++qwq;
	}
	qwq=(qwq-1)*bl; //cout<<qwq<<endl;
	while(sum2[qwq]+g[g[rb].l].cnt2[qwq]-g[lb].cnt2[qwq]<k) {
		k-=sum2[qwq]+g[g[rb].l].cnt2[qwq]-g[lb].cnt2[qwq]; ++qwq;
	}
	for(int i=l;i<=g[lb].sz;i++) --sum1[id[g[lb][i]]],--sum2[g[lb][i]];
	for(int i=1;i<=r;i++) --sum1[id[g[rb][i]]],--sum2[g[rb][i]];
	return qwq;
}

il void update(int x,int v) {
//	//cout<<x<<" "<<v<<endl;
	int b,no=1,pre_v;
	while(g[no].sz<x) {
		x-=g[no].sz,no=g[no].r;
//		//cout<<x<<" "<<no;
	}
	b=no; pre_v=g[b][x];
	if(pre_v==v) return;
	g[b][x]=v;
	for(int i=b;i;i=g[i].r) {
		--g[i].cnt1[id[pre_v]]; --g[i].cnt2[pre_v];
		++g[i].cnt1[id[v]]; ++g[i].cnt2[v];
	}
}

il void split(int x) {
	int b=++tot;
	g[b].r=g[x].r; g[g[x].r].l=b; g[x].r=b; g[b].l=x;
	g[b].sz=g[x].sz/2;
	int tmp=g[x].sz-g[b].sz;
	memcpy(g[b].cnt1,g[x].cnt1,sizeof(g[x].cnt1));
	memcpy(g[b].cnt2,g[x].cnt2,sizeof(g[x].cnt2));
	for(int i=tmp+1;i<=g[x].sz;i++) {
		g[b][i-tmp]=g[x][i];
		--g[x].cnt1[id[g[x][i]]]; --g[x].cnt2[g[x][i]];
		g[x][i]=0;
	}
	g[x].sz=tmp;
}

il void insert(int x,int v) {
	int b,no=1;
	while(g[no].sz<x) {
		if(g[no].r) x-=g[no].sz,no=g[no].r;
		else break;
	}
	b=no; //cout<<b<<endl;
	for(int i=g[b].sz;i>=x;i--) g[b][i+1]=g[b][i];
	g[b][x]=v; ++g[b].sz;
	for(int i=b;i;i=g[i].r) {
		++g[i].cnt1[id[v]]; ++g[i].cnt2[v];
	}
	if(g[b].sz>2*bl) split(b);
}

int main() {
	char op; int x,y,z,las=0;
	n=rd(); bl=400; 
	for(int i=0;i<=N-5;i++) id[i]=i/bl+1; tot=id[n];
	for(int i=1;i<=n;i++) x=rd(),g[id[i]][++g[id[i]].sz]=x;
	m=rd();
	for(int i=1;i<=id[n];i++) g[i].l=i-1,g[i].r=i+1; g[1].l=g[id[n]].r=0;
	for(int x=1;x<=id[n];x++) {
		for(int i=1;i<=g[x].sz;i++) ++g[x].cnt1[id[g[x][i]]],++g[x].cnt2[g[x][i]];
		for(int i=0;i<=N-5;i++) {
			if(!i||id[i]!=id[i-1]) g[x].cnt1[id[i]]+=g[x-1].cnt1[id[i]];
			g[x].cnt2[i]+=g[x-1].cnt2[i];
		}
	}	
	while(m--) {
		op=nex();
		if(op=='Q') {
			x=rd()^las; y=rd()^las; z=rd()^las;
			pr(las=kth(x,y,z)); ED;
		} else if(op=='M') {
			x=rd()^las; y=rd()^las;
			update(x,y);
		} else if(op=='I') {
			x=rd()^las; y=rd()^las;
			insert(x,y); 
		}
	}

	return 0;
}
#include <bits/stdc++.h>

#define N (int)(5e5+5)
#define ED puts("")

using namespace std;

struct node {
	int typ,l,r,val,id;
}q[N],q1[N],q2[N];

int sum[N];
int n,m,tot,cnt,ans[N],lsh[N],pre[N],lcnt;

int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}

void pr(int x) {
	if(x<0) {putchar('-');x=-x;}
	if(x>9) pr(x/10);
	putchar(x%10+'0');
}

int lowbit(int x) {
	return x&(-x);
}

void add(int x,int y) {
	while(x<=n) sum[x]+=y,x+=lowbit(x);
}

int qry(int x) {
	int res=0;
	while(x) res+=sum[x],x-=lowbit(x);
	return res;
}

void solve(int l,int r,int L,int R) {
	if(l>r||L>R) return;
	int cnt1=0,cnt2=0,mid=(l+r)>>1;
	if(l==r) {
		for(int i=L;i<=R;i++) if(q[i].typ==2) ans[q[i].id]=lsh[l];
		return;
	}
	for(int i=L;i<=R;i++) {
		if(q[i].typ==2) {
			int qwq=qry(q[i].r)-qry(q[i].l-1);
			if(q[i].val<=qwq) q1[++cnt1]=q[i];
			else q[i].val-=qwq,q2[++cnt2]=q[i];
		} else {
			if(q[i].val<=mid) add(q[i].l,q[i].r),q1[++cnt1]=q[i];
			else q2[++cnt2]=q[i];
		}
	}
 	for(int i=1;i<=cnt1;i++) if(q1[i].typ==1) add(q1[i].l,-q1[i].r);
 	for(int i=1;i<=cnt1;i++) q[L+i-1]=q1[i];
 	for(int i=1;i<=cnt2;i++) q[cnt1+L+i-1]=q2[i];
 	solve(l,mid,L,L+cnt1-1); solve(mid+1,r,L+cnt1,R);
}

int main() {
	n=rd(); m=rd();
	char op[2]; int l,r,x;
	for(int i=1;i<=n;i++) {
		x=rd();
		q[++tot]=node{1,i,1,x,0};
		lsh[++lcnt]=x; pre[i]=x;
	}	
	for(int i=1;i<=m;i++) {
		cin>>op; l=rd(); r=rd();
		if(op[0]=='Q') {
			x=rd();
			q[++tot]=node{2,l,r,x,++cnt};
		} else {
			q[++tot]=node{1,l,-1,pre[l],0};
			q[++tot]=node{1,l,1,r,0};
			lsh[++lcnt]=r; pre[l]=r;
		}
	}
	sort(lsh+1,lsh+1+lcnt); lcnt=unique(lsh+1,lsh+1+lcnt)-lsh;
	for(int i=1;i<=tot;i++) {
		if(q[i].typ==1) q[i].val=lower_bound(lsh+1,lsh+1+lcnt,q[i].val)-lsh;
	}
	solve(1,lcnt,1,tot);
	for(int i=1;i<=cnt;i++) pr(ans[i]),ED;
	return 0; 
}
#include <bits/stdc++.h>

#define N (int)(1e5+5)

using namespace std;

int rt[N],cx[20],cy[20],a[N];
int n,m,tot,totx,toty;

struct BIT {
	int ls[N*500],rs[N*500],sum[N*500];
	void update(int &cur,int l,int r,int x,int val) {
		if(!cur) cur=++tot;
		sum[cur]+=val;
		if(l==r) return;
		int mid=(l+r)>>1;
		if(x<=mid) update(ls[cur],l,mid,x,val);
		else update(rs[cur],mid+1,r,x,val);
	}
	int query(int l,int r,int k) {
		if(l==r) return l;
		int mid=(l+r)>>1,res=0;
		for(int i=1;i<=totx;i++) res+=sum[ls[cx[i]]];
		for(int i=1;i<=toty;i++) res-=sum[ls[cy[i]]];
		if(k<=res) {
			for(int i=1;i<=totx;i++) cx[i]=ls[cx[i]];
			for(int i=1;i<=toty;i++) cy[i]=ls[cy[i]];
			return query(l,mid,k);
		} else {
			for(int i=1;i<=totx;i++) cx[i]=rs[cx[i]];
			for(int i=1;i<=toty;i++) cy[i]=rs[cy[i]];
			return query(mid+1,r,k-res);
		}
	}
}T;

int lowbit(int x) {
	return x&(-x);
}

void modify(int pos,int x,int val) {
	for(;pos<=n;pos+=lowbit(pos)) T.update(rt[pos],0,1e9,x,val);
}

int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}

int main() {
	n=rd(); m=rd();
	for(int i=1;i<=n;i++) a[i]=rd(),modify(i,a[i],1);
	char op; int l,r,x;
	while(m--) {
		cin>>op; l=rd(); r=rd();
		if(op=='Q') {
			x=rd();
			totx=toty=0;
			for(int i=r;i;i-=lowbit(i)) cx[++totx]=rt[i];
			for(int i=l-1;i;i-=lowbit(i)) cy[++toty]=rt[i];
			printf("%d\n",T.query(0,1e9,x));
		} else {
			modify(l,a[l],-1); modify(l,a[l]=r,1);
		}
	}
}
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>

#define M 505
#define N (int)(3e5+5)

using namespace std;

struct node {
	int x,y,val;
}a[N];

struct ques {
	int x,y,xx,yy,k,id;
}q[N];

int sum[M][M];
int n,m,ans[N];

int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}

bool cmp(node x,node y) {
	return x.val<y.val;
}

int lowbit(int x) {
	return x&(-x);
}

void add(int x,int y,int val) {
	int ny;
	while(x<=n) {
		ny=y;
		while(ny<=n) {
			sum[x][ny]+=val;
			ny+=lowbit(ny);
		}
		x+=lowbit(x);
	}
}

int qry(int x,int y) {
	int res=0,ny;
	if(!x||!y) return 0;
	while(x) {
		ny=y;
		while(ny) {
			res+=sum[x][ny];
			ny-=lowbit(ny);
		}
		x-=lowbit(x);
	}
	return res;
}

int query(int x,int y,int xx,int yy) {
	return qry(xx,yy)+qry(x-1,y-1)-qry(x-1,yy)-qry(xx,y-1);
}

ques t1[N],t2[N];
void solve(int L,int R,int l,int r) {
	if(L==R) {
		for(int i=l;i<=r;i++) {
			//cout<<q[i].k<<":";
			ans[q[i].id]=a[L].val;
		}
		return;
	}
	int mid=(L+R)>>1,cnt1=0,cnt2=0;
	for(int i=L;i<=mid;i++) add(a[i].x,a[i].y,1);
	for(int i=l;i<=r;i++) {
		int res=query(q[i].x,q[i].y,q[i].xx,q[i].yy);
		if(res>=q[i].k) t1[++cnt1]=q[i];
		else q[i].k-=res,t2[++cnt2]=q[i];
	//	cout<<":xgf "<<res<<" "<<i<<" "<<q[i].k<<" "<<L<<' '<<R<<endl;
	}
	for(int i=L;i<=mid;i++) add(a[i].x,a[i].y,-1);
	for(int i=1;i<=cnt1;i++) q[i+l-1]=t1[i];
	for(int i=1;i<=cnt2;i++) q[i+cnt1+l-1]=t2[i];
	solve(L,mid,l,l+cnt1-1); solve(mid+1,R,l+cnt1,r);
}

int main() {
	n=rd(); m=rd();
	int tot=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) 
			a[++tot]=node{i,j,rd()};
	sort(a+1,a+1+tot,cmp);
	for(int i=1;i<=m;i++) {
		q[i]=ques{rd(),rd(),rd(),rd(),rd(),i};
	}
	solve(1,tot,1,m);
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
} 
#include <bits/stdc++.h>

#define N (int)(3e5+5)
#define ls (cur<<1)
#define rs (ls|1)

using namespace std;

struct node {
	int x,y,val,id;
}a[N<<1];

struct edge {
	int nex,to;
}e[N<<1];

bool vis[N];
int head[N],cnt,tot,fa[N],top[N],dep[N],son[N],id[N],sz[N];
int n,m,pre[N],lsh[N],ans[N],l_cnt;

int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}

void add(int x,int y) {
	e[++cnt]=edge{head[x],y};
	head[x]=cnt;
}

void dfs1(int x,int faa) {
	dep[x]=dep[faa]+1; fa[x]=faa; sz[x]=1;
	for(int i=head[x];i;i=e[i].nex) {
		int y=e[i].to;
		if(y==faa) continue;
		dfs1(y,x); sz[x]+=sz[y];
		if(sz[y]>sz[son[x]]) son[x]=y;
	}
}

void dfs2(int x,int tp) {
	top[x]=tp; id[x]=++tot;
	if(son[x]) dfs2(son[x],tp);
	for(int i=head[x];i;i=e[i].nex) {
		int y=e[i].to;
		if(y==fa[x]||y==son[x]) continue;
		dfs2(y,y);
	}
}

int sum[N<<2];
void add(int cur,int l,int r,int x,int val) {
	if(l==r) {
		sum[cur]+=val;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) add(ls,l,mid,x,val);
	else add(rs,mid+1,r,x,val);
	sum[cur]=sum[ls]+sum[rs];
}

int query(int cur,int l,int r,int cl,int cr) {
	if(cl>cr) return 0;
	if(cl<=l&&r<=cr) return sum[cur];
	int mid=(l+r)>>1,res=0;
	if(cl<=mid) res=query(ls,l,mid,cl,cr);
	if(cr>mid) res+=query(rs,mid+1,r,cl,cr);
	return res;
}

int qry(int x,int y) {
	int res=0;
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);//cout<<x<<" "<<y<<endl;
		res+=query(1,1,n,id[top[x]],id[x]);
		x=fa[top[x]];
	}
	if(id[x]>id[y]) swap(x,y);
	return res+query(1,1,n,id[x],id[y]);
}

node q1[N],q2[N];
void solve(int L,int R,int l,int r) {
	if(l>r) return;
	if(L==R) {
		for(int i=l;i<=r;i++) {
			if(a[i].id==-1) continue;
			ans[a[i].id]=lsh[L];
		//	cout<<lsh[L]<<" "<<a[i].val<<endl;
		}
		//cout<<L<<" "<<l<<" "<<r<<endl;
		return;
	}
	int mid=(L+R)>>1,cnt1=0,cnt2=0;
	for(int i=l;i<=r;i++) {
		if(a[i].id==-1) {
			if(a[i].val<=mid) q1[++cnt1]=a[i];
			else q2[++cnt2]=a[i],add(1,1,n,id[a[i].x],a[i].y);
		} else {
			int res=qry(a[i].x,a[i].y);
			if(res>=a[i].val) q2[++cnt2]=a[i];
			else a[i].val-=res,q1[++cnt1]=a[i];
		}
	}
	for(int i=1;i<=cnt2;i++) if(q2[i].id==-1) add(1,1,n,id[q2[i].x],-q2[i].y);
	for(int i=1;i<=cnt1;i++) a[i+l-1]=q1[i];
	for(int i=1;i<=cnt2;i++) a[i+l+cnt1-1]=q2[i];
	solve(L,mid,l,l+cnt1-1); solve(mid+1,R,l+cnt1,r);
}

int main() {
	n=rd(); m=rd();
	int tot1=0,x,y;
	for(int i=1;i<=n;i++) pre[i]=rd(),a[++tot1]=node{i,1,pre[i],-1},lsh[++l_cnt]=pre[i];
	for(int i=1;i<n;i++) x=rd(),y=rd(),add(x,y),add(y,x);
	dfs1(1,0); dfs2(1,1);	
	for(int i=1;i<=m;i++) {
		x=rd();
		if(x==0) {
			x=rd(); y=rd();
			a[++tot1]=node{x,-1,pre[x],-1};
			a[++tot1]=node{x,1,y,-1};
			lsh[++l_cnt]=pre[x]=y;
		} else {
			a[++tot1]=node{rd(),rd(),x,i};
			vis[i]=1;
		}
	}
	sort(lsh+1,lsh+1+l_cnt);// l_cnt=unique(lsh+1,lsh+1+l_cnt)-lsh-1;
	for(int i=1;i<=tot1;i++) {
		if(a[i].id==-1) a[i].val=lower_bound(lsh+1,lsh+1+l_cnt,a[i].val)-lsh;
	} 
	solve(0,l_cnt,1,tot1);
	for(int i=1;i<=m;i++) {
		if(vis[i]) ans[i]?printf("%d\n",ans[i]):puts("invalid request!");
	}
	return 0;
}
  • 莫隊區間第 \(k\)

#include <bits/stdc++.h>

#define N (int)(2e5+5)

using namespace std;

struct ques {
	int l,r,k,id;
}q[N];

int n,m,bl,pos[N],ans[N],a[N],lsh[N],sum[N];

int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}

bool cmp(ques x,ques y) {
	return pos[x.l]==pos[y.l]?pos[x.l]&1?x.r<y.r:x.r>y.r:x.l<y.l;
}

int lowbit(int x) {
	return x&(-x);
}

void add(int x,int w) {
	if(!x) return; 
	while(x<N) {
		sum[x]+=w,x+=lowbit(x);
		//cout<<x<<endl;
	}
}

int qry(int x) {
	int res=0;
	while(x) res+=sum[x],x-=lowbit(x);
	return res;
}

int solve(int k) {
	int l=1,r=n,res=0;
	while(l<=r) {
		int mid=(l+r)>>1;
		if(qry(mid)>=k) res=mid,r=mid-1;
		else l=mid+1;
	}
	return lsh[res];
}

int main() {
	n=rd(); m=rd(); bl=sqrt(n);
	for(int i=1;i<=n;i++) pos[i]=(i-1)/bl+1,lsh[i]=a[i]=rd();
	sort(lsh+1,lsh+1+n);
	for(int i=1;i<=n;i++) a[i]=lower_bound(lsh+1,lsh+1+n,a[i])-lsh;
	//for(int i=1;i<=n;i++) cout<<a[i]<<" "; puts("");
	for(int i=1;i<=m;i++) {
		q[i]=ques{rd(),rd(),rd(),i};
	}
	sort(q+1,q+1+m,cmp);
	int l=0,r=0,cl,cr;
	for(int i=1;i<=m;i++) {
		cl=q[i].l; cr=q[i].r;
	//	cout<<cl<<" "<<cr<<endl;
		while(l<cl) add(a[l++],-1);
		while(l>cl) add(a[--l],1);
		while(r<cr) add(a[++r],1);
		while(r>cr) add(a[r--],-1);
		ans[q[i].id]=solve(q[i].k);
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

參考資料:

https://www.cnblogs.com/fusiwei/p/11432323.html

https://oi-wiki.org/basic/quick-sort/

https://www.luogu.com.cn/blog/2-6-5-3-5/zheng-ti-er-fen-xie-jue-jing-tai-ou-jian-di-k-xiao-di-you-hua


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM