@xiaoziyao
2021-07-29T22:08:01.000000Z
字数 1277
阅读 923
解题报告
CF1404C Fixed Point Removal解题报告:
给定长为 的序列 ,一次操作可以删除一个满足 的位置 ,同时之后的数向前移动一位。
次询问,每次给定 ,求不能删去前 个元素与后 个元素的情况下最多能删除多少元素。( )
套路题。
观察数据范围可知算法复杂度差不多是单 ,根据套路,我们将询问挂在右端点上,并从左往右扫,用数据结构维护左端点为 时的答案。
具体地,我们记 为可以删除的区间左端点为 时,有多少个点无法删除,可以发现加入一个点后我们只需要计算其对一段后缀的贡献。
扫描到 时,若 ,不难发现其一定无法删除,于是直接全局加一。否则,我们二分一个位置使得其前面已经有 个无法删除的位置,将这个位置对应的后缀与 对应的后缀取并并直接更新。
这种操作用树状数组可以轻松维护,二分的时候在树状数组上二分就可以了,时间复杂度 。
#include<stdio.h>
#include<vector>
#define lowbit(x) x&-x
using 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;
}