牛客网 中南林业科技大学第十一届程序设计大赛J题 二分+线段树
https://www.nowcoder.com/acm/contest/124#question
题意 找第一个不小于K的数的下标,然后对它前一个数加一
解析 我们可以维护一个最大值数组 1到 i的 最大值 就是max[ i ] 二分找到最左边的值 但是 找到的前一个加1 要用线段树来维护最大值
但是 这么写会超时。。。
超时代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1000000007,maxn=1e6+50; int sum[maxn<<2]; int a[maxn],n,m; void PushUP(int rt) { sum[rt]=max(sum[rt<<1],sum[rt<<1|1]); } void Build(int l,int r,int rt) { if(l==r) { sum[rt]=a[l]; return; } int m=(l+r)>>1; Build(l,m,rt<<1); Build(m+1,r,rt<<1|1); PushUP(rt); } void Update(int L,int C,int l,int r,int rt) { if(l==r) { sum[rt]+=C; return; } int m=(l+r)>>1; if(L<=m) Update(L,C,l,m,rt<<1); else Update(L,C,m+1,r,rt<<1|1); PushUP(rt); } int Query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) { return sum[rt]; } int m=(l+r)>>1; int ans=-100000000; if(L<=m) ans=max(ans,Query(L,R,l,m,rt<<1)); if(R>m) ans=max(ans,Query(L,R,m+1,r,rt<<1|1)); return ans; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { memset(sum,0,sizeof(sum)); memset(a,0,sizeof(a)); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); } Build(1,n,1); int l,r,k; while(m--) { l=1,r=n; scanf("%d",&k); //cout<<Query(1,n,1,n,1)<<endl; if(Query(1,n,1,n,1)<k) { printf("are you ok\n"); continue; } while(l<=r) { int mid=(l+r)>>1; // cout<<mid<<" "<<Query(1,mid,1,n,1)<<endl; if(Query(1,mid,1,n,1)>=k) r=mid-1; else l=mid+1; } printf("%d\n",l-1); if(l-1) Update(l-1,1,1,n,1); } } }
q的 询问比较多应该是卡了常数 我们要优化一下 因为 线段树查询的时候就是二分 区间最大值是递增的 我们直接利用这个特点来操作
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1000000007,maxn=1e6+50; int sum[maxn<<2]; int a[maxn],n,m; void PushUP(int rt) { sum[rt]=max(sum[rt<<1],sum[rt<<1|1]); } void Build(int l,int r,int rt) { if(l==r) { sum[rt]=a[l]; return; } int m=(l+r)>>1; Build(l,m,rt<<1); Build(m+1,r,rt<<1|1); PushUP(rt); } void Update(int L,int C,int l,int r,int rt) { if(l==r) { sum[rt]+=C; return; } int m=(l+r)>>1; if(L<=m) Update(L,C,l,m,rt<<1); else Update(L,C,m+1,r,rt<<1|1); PushUP(rt); } int query(int L,int R,int l,int r,int rt,int p) { if(l==r) { return l; } int m=(l+r)>>1; if(sum[rt<<1]>=p) //二分查询 return query(L,R,l,m,rt<<1,p); return query(L,R,m+1,r,rt<<1|1,p); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { memset(sum,0,sizeof(sum)); memset(a,0,sizeof(a)); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); } Build(1,n,1); int l,r,k; while(m--) { l=1,r=n; scanf("%d",&k); if(sum[1]<k) { printf("are you ok\n"); continue; } int ans=query(1,n,1,n,1,k); printf("%d\n",ans-1); if(ans-1) Update(ans-1,1,1,n,1); } } }
其实 还有更简单的做法 因为 修改的是前一个值 而且找的是满足条件中最左边的 所以前一个+1 并不会 影响数组的单调性 变得只有前面一个的最大值 更新一下就好了
#include<bits/stdc++.h> using namespace std; int a[1000005],b[1000005]; int n,q,k; int main() { while(~scanf("%d%d",&n,&q)) { int mx=0; for(int i=0;i<n;i++) { scanf("%d",&a[i]); b[i]=mx=max(mx,a[i]); } while(q--) { scanf("%d",&k); int l=lower_bound(b,b+n,k)-b; if(l==n){ printf("are you ok\n"); continue; } printf("%d\n",l); if(l==0)continue; a[l-1]++; b[l-1]=max(a[l-1],b[l-1]); } } }