acm-(主席树)Codeforces Round #668 (Div. 1) C. Fixed Point Removal

题面
传送门
首先假设前 i i i个数有 f i f_i fi个可以被删除,那么 f i = f i − 1 + [ i − a i ≥ 0   o r   f i ≥ i − a i ] f_{i}=f_{i-1}+[i-a_i\ge 0\,or\,f_i\ge i-a_i] fi=fi1+[iai0orfiiai]

证明:我们可以先删除前 i − 1 i-1 i1个数中的 i − a i i-a_i iai个可删的数,此时刚好可以把 a i a_i ai删除,于是把 a i a_i ai删除以后再去删除前面剩下的可删除的数即可。

设最多长度为 l i m [ i ] lim[i] lim[i]的前缀被锁定(即无法操作)后能够保证 a i a_i ai仍然能被删除,考虑二分 l i m [ i ] lim[i] lim[i]的值,因为 l i m [ i ] lim[i] lim[i]小的时候 a i a_i ai能被删除的可能性更大。 c h e c k check check的时候考虑对于前 i − 1 i-1 i1个数假设有 c c c个数 l i m lim lim大于等于 m i d mid mid,若 c ≥ i − a i c\ge i-a_i ciai那么我们就知道 l i m [ i ] ≥ c lim[i]\ge c lim[i]c

由于需要查询区间大于等于 x x x的数个数,因此需要用到主席树,由于数字是 0 ∼ n − 1 0\sim n-1 0n1,不妨加个一即可。

对于查询而言,我们只需要查询 [ x , n − y ] [x,n-y] [x,ny]区间内满足 l i m ≥ x lim\ge x limx的数有多少个即可。

int a[maxn],lim[maxn],t[maxn<<5],tot=0,root[maxn],l[maxn<<5],r[maxn<<5];

void build(int &rt,int tl,int tr){
	rt=++tot;
	if(tl==tr)return;
	int mid=tl+tr>>1;
	build(l[rt],tl,mid);
	build(r[rt],mid+1,tr);
}
void add(int &rt,int pre,int tl,int tr,int x){
	rt=++tot;
	t[rt]=t[pre]+(x!=-1);l[rt]=l[pre],r[rt]=r[pre];
	if(tl==tr)return;
	int mid=tl+tr>>1;
	if(x<=mid)add(l[rt],l[pre],tl,mid,x);
	else add(r[rt],r[pre],mid+1,tr,x);
}
int qry(int L,int R,int tl,int tr,int x){
	int sm=t[R]-t[L];
	if(tl>=x)return sm;
	int mid=tl+tr>>1,ans=0;
	if(mid>=x)ans+=qry(l[L],l[R],tl,mid,x);
	ans+=qry(r[L],r[R],mid+1,tr,x);
	return ans;
}
int main(){
	int n=rd(),q=rd();
	build(root[0],1,n);
	FOR(i,1,n+1){
		int u=rd();
		u=i-u;
		if(u<0){
			lim[i]=-1;
			add(root[i],root[i-1],1,n,-1);
		}
		else{
			int l=0,r=i-1,ans=-1;
			while(l<=r){
				int mid=l+r>>1;
				if(qry(root[0],root[i-1],1,n,mid+1)>=u){
					l=mid+1;
					ans=mid+1;
				}else r=mid-1;
			}
			lim[i]=ans;
			add(root[i],root[i-1],1,n,lim[i]);
		}
	}
	while(q--){
		int x=rd(),y=rd();
		x++;
		int ans=qry(root[x-1],root[n-y],1,n,x);
		wrn(ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值