题目链接、
我才不是毒瘤出题人
Time Limit: 1000 ms Memory Limit: 65536 KiB
Problem Description
有一个长度为n的初始序列a,下标从1到n。
现在你有两种操作,
1:下标为k的数加1
2:序列中第k小的数是多少?
Input
第一行两个数n,m.表示序列长度和操作次数。
第二行n个数,表述序列的初始值。
接下来m行,每行两个数x,k。x表示操作种类数,1代表第一种操作,2代表第二种操作。
1<=n<=2e5,1<=m<=1e7,0<=a[i]<=1e7,1<=x<=2,1<=k<=n.
Output
对每次询问,输出第k大的数的值。
Sample Input
5 5 1 2 3 4 5 2 3 1 1 2 1 2 2 2 3
Sample Output
3 2 2 3
Hint
Source
QYQ
emmm
O(1)修改,O(1)查询
很神奇的优化思路、
维护一个排好序 的数组,每次询问即输出B[k],修改的影响:
移动并不好处理,直接移动是不行的
一个神奇的想法即
L[val]表示值为val的数在排好序的数组中出现的左端点
R[val]同理表示右端点
来看怎么优化的,每次A[k]++,那,我们先拿出A[k]的值
可以直接拿这个值通过L,R数组,对应到B数组里,为了维持
B数组的有序,我们要改动的值就是R[val]咯,具体可以看代码了
/*author:revolIA*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read(){
int Nig = 1,c = getchar(),ans = 0;
while(!isdigit(c) && c != '-'){
c = getchar();
}
if(c == '-')Nig = -1,c = getchar();
while(isdigit(c)){
ans = ans*10+c-'0';
c = getchar();
}
return ans*Nig;
}
const int maxn = 2e5+7,maxm = 1e7+7;
int l[maxm],r[maxm],n,q,opt,k;
int rak[maxn];
struct Node{int val,id;}A[maxn],B[maxn];
int cmp(Node A,Node B){
return A.val<B.val;
}
int main(){
n = read();
q = read();
for(int i=1;i<=n;i++){
A[i].val = read();
A[i].id = i;
B[i] = A[i];
}
sort(B+1,B+1+n,cmp);
for(int i=1;i<=n;i++){
l[B[i].val] = i;
while(i+1<=n && B[i].val == B[i+1].val)
i++;
r[B[i].val] = i;
}
while(q--){
opt = read();
k = read();
if(opt == 1){
int val = A[k].val++;
B[r[val]].val ++;
if(!r[val+1])
r[val+1] = r[val];
l[val+1] = r[val]--;
}else{
printf("%d\n",B[k].val);
}
}
return 0;
}