给定一个长度为 N 的整数数组。你的程序需要按照输入顺序处理如下两种操作:
1.修改。修改操作带有两个参数 p 和 x ,表示将数组中下标为 p 的元素修改为 x ;
2.查询。查询操作带有三个参数 L,R和 k ,表示查询数组中从下标为 L 的元素开始到下标为 R 的元素为止(两端均包含)的这个子数组中数值从小到大排第 k 的整数。
输入描述
测试用例输入第一行为一个整数N [1,1000 000] ,表示数组的长度。
第二行为 N 个以空格分隔的整数,表示初始数组的每一项。
第三行为一个整数 Q [100 000],表示操作的数量。
从第四行开始的 Q 行,每行包含若干个以空格分隔的整数。其中第一个整数表示操作的类型,1表示修改操作,2表示查询操作;若操作为修改操作,接下来包含两个整数p,x (0<=p<N 0<=x<=1 000 000 000 ) ,含义如前述所示;若操作为查询操作,接下来包含三个整数 L,R和 k (0<=L<=R<N 1<=k<=R-L+1),含义如前述所示。
保证数组中的每个元素在任意时刻均在 [0,1 000 000 000]范围内。
保证所有查询中子数组的长度之和不超过40 000 000 。
输出描述
对于输入中的每一个查询操作,你的程序应该在单独的一行上输出查询的结果。
样例说明
对于题目中给出的样例,其第一个操作为 2 1 3 2
,表示查询下标从 1 - 3 范围内,即数组 [2, 3, 4] 中数值排名第二大的元素,即为 3;第二个操作为 1 2 2
,表示修改下标为 2 的元素为 2;第三个操作为2 1 3 2
,表示查询下标从 1 - 3 范围内,即数组 [2, 2, 4] (注意此时下标为 2 的元素已经被修改为 2)中数值排名第二大的元素,即为 2。
注意,计算排名时,相同的整数要进行重复计算。例如样例中的 [2, 2, 4],计算排名时 2 排第 1 和 2,4 排第 3。
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 以文本方式显示
|
以文本方式显示
|
1秒 | 64M | 0 |
#include<stdio.h>
#include<stdlib.h>
//初学时写的代码,非常冗杂
int min(int a,int b,int c,int l,int mid,int r)
{
int q[3]={a,b,c},i,temp,j;
for(i=0;i<2;i++)
{
for(j=i;j<2;j++)
{
if(q[i]>q[i+1])
{
temp=q[i];
q[i]=q[i+1];
q[i+1]=temp;
}
}
}
if(q[1]==a) return l;
if(q[1]==b) return mid;
if(q[1]==c) return r;
}
int quickselect(int c[],int l,int r,int k)
{
int p,j,i,temp; //HoarePartition
int mid,need;
mid=(l+r)/2;
need=min(c[l],c[mid],c[r],l,mid,r); //找到头c[l]、尾c[r]、中c[mid]三数的中值的下标 need
//这里随机选取中轴更好
temp=c[need];
c[need]=c[l];
c[l]=temp; //调换c[need],c[l]
p=c[l]; //中轴
i=l;
j=r+1;
do
{
do
{
i++;
}while(c[i]<p);
do
{
j--;
}while(c[j]>p);
temp=c[i];
c[i]=c[j];
c[j]=temp;
}while(i<j);
temp=c[i];
c[i]=c[j];
c[j]=temp;
temp=c[l];
c[l]=c[j];
c[j]=temp;
if(j==l+k-1) return c[j];
else if(j>l+k-1) quickselect(c,l,j-1,k);
else quickselect(c,j+1,r,l+k-1-j);
}
int main(){
int n,i,m,t,j,result;
scanf("%d",&n);
int a[n];
for(i=0;i<n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
int b[m][4];
for(i=0;i<m;i++)
{
scanf("%d",&b[i][0]);
if(b[i][0]==1) t=2;
else t=3;
for(j=1;j<=t;j++) scanf("%d",&b[i][j]);
}
int l,r,k,u;
if(n>100000) //这里根据n的大小决定开静态数组还是动态数组完全是初学时多余的操作,开个全局数组就好了
{
int *c;
int nc;
for(i=0;i<m;i++)
{
if(b[i][0]==1) a[b[i][1]]=b[i][2]; //alter
if(b[i][0]==2) //inquire
{
l=b[i][1];
r=b[i][2];
k=b[i][3];
nc=r-l+1;
c=(int*)malloc(nc*sizeof(int));
for(j=l,u=0;j<=r;j++,u++) c[u]=a[j];
result=quickselect(c,0,u-1,k);
free(c);
printf("%d\n",result);
}
}
}
else
{
int c[n];
for(i=0;i<m;i++)
{
if(b[i][0]==1) a[b[i][1]]=b[i][2]; //alter
if(b[i][0]==2) //inquire
{
l=b[i][1];
r=b[i][2];
k=b[i][3];
for(j=l,u=0;j<=r;j++,u++) c[u]=a[j];
result=quickselect(c,0,u-1,k);
printf("%d\n",result);
}
}
}
}