CF1405E Fixed Point Removal(线段树+二分)
第一点可以想到的是,对于某个位置i上的数,他能不能被删除只和前面的数的能不能删除掉-(a[i]-i)的数有关。前提是他要a[i]-i小于0
现在的问题是,因为他有很多询问,因为左区间的限定,我们对每个询问需要知道每个i对于当前左区间l-i之间能删除多少个数,这样我们才能知道对于当前区间这个点能不能删除。
因此我们想到每个左区间的端点都是十分重要的,因此我们考虑维护一个线段树,每个点的意义都是以i为左区间能删除的数的大小。
对于每个点,我们在线段树上二分查询到离他最近的能满足条件的位置,把点的贡献加到对应位置上,这样做是最优的。
另外,这需要对询问排序,因为我们不能让后面的点对当前位置的答案产生错误的贡献
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=3e5+10; int n,q; int a[N]; struct Q{ int l,r; int id; }s[N]; struct node{ int l,r; int sum; }tr[N<<2]; bool cmp(Q a,Q b){ return a.r>b.r; } void build(int u,int l,int r){ if(l==r){ tr[u]={l,r,0}; } else{ tr[u]={l,r,0}; int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } } void modify(int u,int l,int x){ if(tr[u].l==tr[u].r){ tr[u].sum+=x; return ; } int mid=tr[u].l+tr[u].r>>1; if(l<=mid) modify(u<<1,l,x); else modify(u<<1|1,l,x); tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum; } int query(int u,int l,int r){ if(tr[u].l>=l&&tr[u].r<=r){ return tr[u].sum; } int mid=tr[u].l+tr[u].r>>1; int ans=0; if(l<=mid){ ans+=query(u<<1,l,r); } if(r>mid) ans+=query(u<<1|1,l,r); return ans; } int ans[N]; int get(int u,int l){ if(tr[u].l==tr[u].r){ return tr[u].l; } int mid=tr[u].l+tr[u].r>>1; if(tr[u<<1|1].sum>=l){ return get(u<<1|1,l); } else{ return get(u<<1,l-tr[u<<1|1].sum); } } int main(){ cin>>n>>q; int i; for(i=1;i<=n;i++){ cin>>a[i]; a[i]-=i; } for(i=1;i<=q;i++){ int x,y; cin>>x>>y; s[i]={x,y,i}; } sort(s+1,s+1+q,cmp); int cur=1; int tot=0; build(1,1,n); for(i=1;i<=q;i++){ while(cur<=n-s[i].r){ if(a[cur]==0) modify(1,cur,1),tot++; else if(a[cur]<0&&-a[cur]<=tot){ modify(1,get(1,-a[cur]),1); tot++; } cur++; } ans[s[i].id]=query(1,s[i].l+1,n-s[i].r); } for(i=1;i<=q;i++) cout<<ans[i]<<endl; return 0; }
没有人不辛苦,只有人不喊疼