【模拟试题】逛公园

【模拟试题】逛公园

Description

窝窝头经常陪碳原子去公园玩,也就是所谓的遛狗啦…在窝窝头家附近有一条“公园路”,路的一边从南到北依次排着n个公园,碳原子早就看花了眼,自己也不清楚该去哪些公园玩了。
一开始,碳原子就根据公园的风景给每个公园打了分-.-。窝窝头为了省事,每次遛狗的时候都会事先规定一个范围,碳原子只可以在第a个和第b个公园之间(包括a、b两个公园)选择得分第k大的公园玩。同时,由于一些公园的景观会有所改变,所以,碳原子的打分也可能会有一些变化。
那么,就请你来帮碳原子选择公园吧。

Input

第一行,两个整数N和M,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。
接下来N行,每行一个整数,依次给出碳原子开始时对公园的打分。
接下来M行,每行三个整数。第一个整数K,1或2。K=1表示,窝窝头要带碳原子出去玩,接下来的3个整数a,b和k给出了选择公园的范围,窝窝头想知道a到b这些公园中得分第k大的公园的得分是哪个;K=2表示,碳原子改变了对某个公园的打分,接下来的两个整数p和s,表示碳原子对第p个公园的打分变成了s(1≤p≤N)。

Output

碳原子每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分第k大的值。

Sample Input

5 3

1 2 -3 4 5

1 1 5 3

2 3 -1

1 1 3 2

Sample Output

2

1

Hint

park.in
5 4
2 1 5 4 3
1 1 5 3
1 1 3 1
2 1 4
1 1 3 2
park.out
3
5
4
【数据范围】
对于10%的数据,1<=N,M<=1000
对于30%的数据,1<=N,M<=20000
对于100%的数据,1<=N,M<=100000,0<=|某个公园的得分|<=N,K=1或2, 1<=a<=b<=N,1<=p<=N,1<=k<=b-a+1

Solution

带修改的区间第k大,用主席树试一下。

CODE

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=1000005;
inline int read(){
	char c;int rec=0,f=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')f=-1;
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec*f;
}
int n,m,val[N];
int root[N],cnt,llen,rlen,len[2];
struct Presistent_Segtree{int F,s[2],cnt;}tree[N*20];
int V[2][N];
inline int lowbit(int x){return x&-x;}
inline int Add(int val,int L,int R,int pos,int f){
	if(pos==0)pos=++cnt;
	tree[pos].cnt+=f;
	if(L==R)return pos;
	int mid=(L+R)>>1;
	if(mid>=val)tree[pos].s[0]=Add(val,L,mid,tree[pos].s[0],f);
    else tree[pos].s[1]=Add(val,mid+1,R,tree[pos].s[1],f);
    return pos;
}
inline void Insert(int x,int val,int f){
	while(x<=n){
		root[x]=Add(val,0,2*N,root[x],f);
		x+=lowbit(x);
	}return ;
}
inline int Get_Pos(int x,int f){
	int c=0,i;
	for(i=x;i>=1;i-=lowbit(i))V[f][++c]=root[i];
	return c;
}
int Get_sum(int f){
	int i,sum=0;
	for(i=1;i<=len[f];i++)
	    sum+=tree[tree[V[f][i]].s[0]].cnt;
	return sum;
}
inline int Ask(int x,int y,int Left,int Right,int k){
	if(Left==Right) return Left;
	int p=Get_sum(1)-Get_sum(0),i,mid=(Left+Right)/2;
	if(k<=p) {
		for(i=1;i<=len[0];i++) V[0][i]=tree[V[0][i]].s[0];
	    for(i=1;i<=len[1];i++) V[1][i]=tree[V[1][i]].s[0];
	    return Ask(x,y,Left,mid,k);
	}
	else {
		for(i=1;i<=len[0];i++) V[0][i]=tree[V[0][i]].s[1];
	    for(i=1;i<=len[1];i++) V[1][i]=tree[V[1][i]].s[1];
	    return Ask(x,y,mid+1,Right,k-p);
	}
}
int main(){
	n=read();m=read();
	int i,f,x,y;
	for(i=1;i<=n;i++)val[i]=read(),val[i]+=N;
	for(i=1;i<=n;i++)Insert(i,val[i],1);
	for(i=1;i<=m;i++){
		f=read();
		if(f==1){
			x=read();y=read();f=read();f=(y-x+1)-f+1;
			len[0]=Get_Pos(x-1,0);len[1]=Get_Pos(y,1);
   			cout<<Ask(x-1,y,0,2*N,f)-N<<'\n';
		}
		else if(f==2){
			x=read();Insert(x,val[x],-1);
			val[x]=read();val[x]+=N;Insert(x,val[x],1);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值