题目描述
一个长度为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的话,则是无解的.否则递归左右子树寻找即可
修改操作 线段树单点更新
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
ll arr[maxn];
struct SegTree
{
ll Max[maxn<<2];
void init() {
memset(Max,0,sizeof(Max));
}
void push_up(int rt) {
Max[rt] = max(Max[rt<<1],Max[rt<<1|1]);
}
void build(ll arr[],int l,int r,int rt) {
if(l == r) {
Max[rt] = arr[l];
return ;
}
int mid = (l + r) >> 1;
build(arr,l,mid,rt<<1);
build(arr,mid+1,r,rt<<1|1);
push_up(rt);
}
void update(int pos,ll val,int l,int r,int rt) {
if(l == r) {
Max[rt] += val;
return ;
}
int mid = (l + r)>>1;
if(pos <= mid) update(pos,val,l,mid,rt<<1);
else update(pos,val,mid+1,r,rt<<1|1);
push_up(rt);
}
int query(ll k,int l,int r,int rt) {
if(Max[rt] < k) return 0;
if(l == r) {
return l;
}
int mid = (l+r)>>1;
if(Max[rt<<1] >= k) return query(k,l,mid,rt<<1);
else if(Max[rt<<1|1] >= k) return query(k,mid+1,r,rt<<1|1);
return 0;
}
}seg;
int main()
{
int n,q;
while(~scanf("%d%d",&n,&q))
{
for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);
seg.init();
seg.build(arr,1,n,1);
while(q--) {
ll k;scanf("%lld",&k);
int res = seg.query(k,1,n,1);
if(!res) { printf("are you ok\n");continue;}
printf("%d\n",res-1);
if(res != 1) seg.update(res-1,1,1,n,1);
}
}
return 0;
}