题目描述
一个长度为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; }