@xiaoziyao
2021-07-29T14:08:01.000000Z
字数 1277
阅读 1359
解题报告
CF1404C Fixed Point Removal解题报告:
给定长为 的序列 ,一次操作可以删除一个满足 的位置 ,同时之后的数向前移动一位。
次询问,每次给定 ,求不能删去前 个元素与后 个元素的情况下最多能删除多少元素。( )
套路题。
观察数据范围可知算法复杂度差不多是单 ,根据套路,我们将询问挂在右端点上,并从左往右扫,用数据结构维护左端点为 时的答案。
具体地,我们记 为可以删除的区间左端点为 时,有多少个点无法删除,可以发现加入一个点后我们只需要计算其对一段后缀的贡献。
扫描到 时,若 ,不难发现其一定无法删除,于是直接全局加一。否则,我们二分一个位置使得其前面已经有 个无法删除的位置,将这个位置对应的后缀与 对应的后缀取并并直接更新。
这种操作用树状数组可以轻松维护,二分的时候在树状数组上二分就可以了,时间复杂度 。
#include<stdio.h>#include<vector>#define lowbit(x) x&-xusing namespace std;const int maxn=300005;int n,m;int a[maxn],ans[maxn],t[maxn];vector< pair<int,int> >v[maxn];void update(int x,int v){for(int i=x;i<=n;i+=lowbit(i))t[i]+=v;}int query(int x){int res=0;for(int i=x;i;i-=lowbit(i))res+=t[i];return res;}int getkth(int k){int res=0;for(int i=18;i>=0;i--)if(res+(1<<i)<=n&&k>t[res+(1<<i)])k-=t[res+(1<<i)],res+=(1<<i);return res+1;}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);x++,y=n-y;if(x<=y)v[y].push_back(make_pair(x,i));}for(int i=1,tot=0;i<=n;i++){if(i-a[i]>=0){tot++;int pos=getkth(a[i]);if(pos<=i)update(pos,1);else update(i+1,1);}for(int j=0;j<v[i].size();j++)ans[v[i][j].second]=tot-query(v[i][j].first);}for(int i=1;i<=m;i++)printf("%d\n",ans[i]);return 0;}
