BZOJ 4631: 踩气球

本文介绍了一种结合线段树和并查集的数据结构算法,用于处理区间更新和查询问题。通过为每个点建立权值线段树,实现区间左端点值的插入与更新,同时利用并查集维护左侧最近未删除位置,解决复杂区间操作。适用于动态区间问题,如区间删除和查询最右侧非空位置。

对于每个点开一棵权值线段树,以区间的左端点为值插入右端点的权值线段树中,并查集维护i左边第一个不被删空的位置,为F[i]

当一个点被删空的时候,这棵线段树中>F[i]的位置即为答案,清空以后把i和F[i]这两棵线段树合并

#include<cstdio>
using namespace std;
int cnt,Lastans,F[1000005],a[1000005],ls[2000005],rs[2000005],sz[2000005],root[100005];
int find(int x){
	if (!a[F[x]]) F[x]=find(F[x]);
	return F[x];
}
void update(int t){
	sz[t]=sz[ls[t]]+sz[rs[t]];
}
void del(int t,int l,int r,int x,int y){
	if (!t) return;
	if (r<x || l>y) return;
	if (l>=x && r<=y){
		Lastans+=sz[t];
		sz[t]=0;
		return;
	}
	int mid=(l+r)>>1;
	del(ls[t],l,mid,x,y);
	del(rs[t],mid+1,r,x,y);
	update(t);
}
void insert(int &t,int l,int r,int x){
	if (!t) t=++cnt;
	sz[t]++;
	if (l==r) return;
	int mid=(l+r)>>1;
	if (x<=mid) insert(ls[t],l,mid,x);
	else insert(rs[t],mid+1,r,x);
}
int merge(int x,int y){
	if (!x || !y) return x^y;
	sz[x]+=sz[y];
	ls[x]=merge(ls[x],ls[y]);
	rs[x]=merge(rs[x],rs[y]);
	return x;
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for (int i=1; i<=n; i++) scanf("%d",&a[i]);
	for (int i=1; i<=n; i++) F[i]=i-1;
	for (int i=1; i<=m; i++){
		int l,r;
		scanf("%d%d",&l,&r);
		insert(root[r],1,n,l);
	}
	a[0]=1;
	int q;
	scanf("%d",&q);
	while (q--){
		int x;
		scanf("%d",&x);
		x=(x+Lastans-1)%n+1;
		a[x]--;
		if (!a[x]){
			int fx=find(x);
			del(root[x],1,n,fx+1,n);
			root[fx]=merge(root[fx],root[x]);
		}
		printf("%d\n",Lastans);
	}
	return 0;
}

  

转载于:https://www.cnblogs.com/silenty/p/9838515.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值