are you ok?
题目描述
一个长度为n的数组a,数组下标从0开始。现在要求你查询从左到右第一个不小于k的数字a[i], 输出i,并且马上把a[i-1]++;如果你找到的a[i]中的i等于0,那么a[0-1]是非法的,因此只要输出i就行了,不进行a[i-1]++;如果你在数组中找不到一个数字不小于k,则输出”are you ok ”
输入描述:
多组输入,输入直到遇到EOF为止;
第一行输入两个整数n和q,表示数组a中有n个整数,q表示q次查询;
第二行输入n个整数;
第三行到后2+q行,每行输入一个数字k,表示要求你查询从左到右第一个不小于k的数字并马上输出。
注意:1 < n, q <= 1e6, a[i]和k是一个int型的整数
输出描述:
见输入。
示例1
输入
复制
3 4
1 2 8
2
2
8
9
输出
复制
1
0
2
are you ok
本来以为要写一个线段树套主席树,突然一想,其实我们维护最大值即可。为什么呢,我们每次考虑大区间的最大值,如果最大值大于等于k,那么这个区间肯定存在一个合法的值,然后我们递归考虑左区间,如果左区间的最大值大于等于k,那么我们递归考虑左区间,不然递归考虑右区间。而且因为我们优先考虑左区间,所以找到的必然是最左边的值。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e6+10;
int n,q,a[N];
struct node{
int l,r,max;
}t[N<<2];
inline void push_up(int p){
t[p].max=max(t[p<<1].max,t[p<<1|1].max);
}
void build(int p,int l,int r){
t[p].l=l; t[p].r=r;
if(l==r){
t[p].max=a[l]; return ;
}
int mid=l+r>>1;
build(p<<1,l,mid); build(p<<1|1,mid+1,r);
push_up(p);
}
void change(int p,int x){
if(t[p].l==t[p].r){
t[p].max++; return ;
}
int mid=t[p].l+t[p].r>>1;
if(x<=mid) change(p<<1,x);
else change(p<<1|1,x);
push_up(p);
}
int ask(int p,int x){
if(t[p].l==t[p].r){
return t[p].l;
}
int mid=t[p].l+t[p].r>>1;
if(t[p<<1].max>=x) return ask(p<<1,x);
else return ask(p<<1|1,x);
}
signed main(){
while(~scanf("%d %d",&n,&q)){
for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n);
while(q--){
int x; scanf("%d",&x);
if(t[1].max<x){
puts("are you ok"); continue;
}
int id=ask(1,x); printf("%d\n",id-1);
if(id!=1) change(1,id-1);
}
}
return 0;
}