[ZJOI2013] K 大数查询

传送门

沃市区。

题目思路

整体二分加线段树。二分答案(第 \(c\) 大的数具体是多少,范围是 \([L,R]\))后,我们就处理当前含有的询问。

设 \(mid=L+R>>1\)(注意到这种写法在此题十分重要,因为 \((L+R)/2\) 这个写法向 \(0\) 取整)。

插入 \(x\) 的操作如果满足 \(x \le mid\),则该插入操作会影响下一层左侧(答案范围是 \([L,mid]\) )的答案。故应该加入线段树。

询问操作就板板的。

注意到这道题目是求第 \(k\) 大的数字,可以取反后套熟悉的板子。最后输出时记得恢复。

🍭🍭错误:

真是连续做两道都忘了 else 以及其中的加入操作。

else{
	int tot=query(1,1,n,q[i].l,q[i].r);
	if(tot>=q[i].c)	q1[++p1]=q[i];
	else	q[i].c-=tot,q2[++p2]=q[i];
}

AC Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+5;
int n,m,qcnt,cnt,t[N<<2],tag[N<<2],ans[N];
struct node{
	int bz,l,r,c,id;
}q[N],q1[N],q2[N];
int lc(int p){return p<<1;}
int rc(int p){return p<<1|1;}
void pushup(int p){
	t[p]=t[lc(p)]+t[rc(p)];
}
void pushdown(int p,int l,int r){
	if(tag[p]){
		int mid=l+r>>1;
		tag[lc(p)]+=tag[p];
		tag[rc(p)]+=tag[p];
		t[lc(p)]+=(mid-l+1)*tag[p];
		t[rc(p)]+=(r-mid)*tag[p];
		tag[p]=0;
	}
}
void update(int p,int l,int r,int L,int R,int d){
	if(L<=l&&r<=R){
		tag[p]+=d;
		t[p]+=d*(r-l+1);
		return ;
	}
	pushdown(p,l,r);
	int mid=l+r>>1;
	if(L<=mid)	update(lc(p),l,mid,L,R,d);
	if(mid<R)	update(rc(p),mid+1,r,L,R,d);
	pushup(p);	
}
int query(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R)	return t[p];
	pushdown(p,l,r);
	int mid=l+r>>1,ans=0;
	if(L<=mid)	ans+=query(lc(p),l,mid,L,R);
	if(mid<R)	ans+=query(rc(p),mid+1,r,L,R);
	return ans;
}
void solve(int l,int r,int L,int R){
	if(L==R){
		for(int i=l;i<=r;i++)	ans[q[i].id]=L;
		return ;
	}
	int mid=L+R>>1,p1=0,p2=0;
	for(int i=l;i<=r;i++){
		if(q[i].bz==1){
			if(q[i].c<=mid){
				update(1,1,n,q[i].l,q[i].r,1);
				q1[++p1]=q[i];
			}
			else	q2[++p2]=q[i];
		}
		else{
			int tot=query(1,1,n,q[i].l,q[i].r);
			if(tot>=q[i].c)	q1[++p1]=q[i];
			else	q[i].c-=tot,q2[++p2]=q[i];
		}
	}
	for(int i=l;i<=r;i++){
		if(q[i].bz==1&&q[i].c<=mid)	update(1,1,n,q[i].l,q[i].r,-1);
	}
	for(int i=1;i<=p1;i++)	q[l+i-1]=q1[i];
	for(int i=1;i<=p2;i++)	q[l+p1+i-1]=q2[i];
	solve(l,l+p1-1,L,mid);solve(l+p1,r,mid+1,R);
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1,op,l,r,c;i<=m;i++){
		cin>>op>>l>>r>>c;
		if(op==1)	q[i]={op,l,r,-c,0};//取反,求第 c 小 
		else	q[i]={op,l,r,c,++qcnt};
	}
	solve(1,m,-n,n);
	for(int i=1;i<=qcnt;i++)	cout<<-ans[i]<<"\n";
	return 0;
}
posted @ 2026-10-08 21:26  深申  阅读(6)  评论(0)    收藏  举报