ZOJ-2112 Dynamic Rankings 分块+二分查找

题目:

给定一个含有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继续回答上面的问题。

Input

第一个数表示数据组数。 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。 第二行有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。

Output:

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

解题思路:

首先,我们得知道,一段区间第K大的数只大于等于区间内中的K个数.故只用二分查找出最小的数在这个区间内正好有K个数小于等于它即可. 对于每一个块都维护顺序(即对每一个块的内部元素排序)以便之后可以快速得出这个块内小于等于一个值的元素有多少个. 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define M 1000000000//数值上限
using namespace std;
int S,P[60005],B[60005],C[60005];
struct node {
	int id,d;
	bool operator <(const node &_) {
		return d<_.d;
	}
} A[61005];
void turn(int i,int t) {
	int k=i/S;
	A[P[i]].d=t;
	B[i]=t;
	sort(A+k*S,A+(k+1)*S);
	for(int i=k*S; i<(k+1)*S; i++)P[A[i].id]=i,C[i]=A[i].d;//每次排序后都要维护编号
}
int find(int l,int r,int d) {
	int ka=l/S,kb=r/S;//块编号
	if(ka==kb) {
		return B[r-d+1];
	} else {
		//return 0;
		int L=0,R=M,ans=0;
		while(L<=R) {
			int mid=(L+R)>>1,sum=0;
			//printf("!%d %d\n",L,R);
			for(int i=l; i<(ka+1)*S; i++)sum+=(B[i]<=mid);
			for(int i=kb*S; i<=r; i++)sum+=(B[i]<=mid);//统计残缺快内小于等于mid的数
			//puts("!");
			for(int i=ka+1; i<kb; i++)sum+=upper_bound(C+i*S,C+i*S+S,mid)-C-i*S;//查找C内有多少个小于等于mid的数.
			if(sum>=d)ans=mid,R=mid-1;
			else L=mid+1;
		}
		return ans;
	}
}
char s[5];
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		int n,m,k;
		scanf("%d%d",&n,&m);
		if(n==1) {//特判,能不能删掉,读者自行尝试.
			S=1;
			k=1;
		}else{
			S=sqrt(n*log(n)/log(2));//最优的块的大小
			//printf("%d\n",S);
			k=n/S;
		}
		//printf("k=%d\n",k);
		for(int i=1; i<=n; i++)scanf("%d",&A[i].d),A[i].id=i,B[i]=A[i].d;//B内存正确顺序的元素的值
		//sort(A+1,A+S);
		A[0].d=0;
		A[0].id=0;
		for(int i=0; i<=k; i++) {
			//printf("%d %d\n",S*i,(i+1)*S-1);
			sort(A+S*i,A+(i+1)*S);//初始化块内元素有序
		}
		//for(int i=1; i<=n; i++)printf("%d ",A[i].d);
		for(int i=1; i<=n; i++)P[A[i].id]=i,C[i]=A[i].d;//P为元素的编号与其排序后的位置的一个映射
		//puts("");
		while(m--) {
			scanf("%s",s);
			if(s[0]=='C') {
				int a,d;
				scanf("%d%d",&a,&d);
				turn(a,d);
			} else {
				int l,r,d;
				scanf("%d%d%d",&l,&r,&d);
				printf("%d\n",find(l,r,d));
			}
		}
	}
	return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值