hdu6521 Party(线段树二分+思维)

本文介绍了一种利用线段树处理区间操作的问题,具体是关于n个人在数轴上的区间[m]内进行多次操作,每次操作会使指定区间内的所有人两两相识。通过证明区间具有非严格单增性质,采用线段树维护区间最大值和最小值,通过二分查找确定每个区间可以拓展的范围,从而计算每轮新建立的相识对数。文章提供了详细的思路解析和代码实现。
摘要由CSDN通过智能技术生成

题目

n(n<=5e5)个人,对应数轴上[1,n]的区间

m(m<=5e5)次操作,第i轮给出li,ri,会让[li,ri]内区间的人两两认识,

特别地,如果两个人在第[1,i-1]轮就已经认识了,则忽略

对于第i次,回答多少pair是这一轮认识的

思路来源

https://qleonardo.blog.csdn.net/article/details/89409912

题解

考虑对于第x个人,包含x的区间,可以使x认识的人的左右端点分别往左拓和往右拓,显然对应一个连续的区间

设x对应的区间为[lx,rx],则有一个性质,

若i<j,则li<=lj,ri<=rj,即区间是整体右移的,有非严格单增的性质

证明也很好证,不妨只证li<=lj,

反证法设lj<li,则j向左穿过了i认识了一个比li更靠左的人,这显然不可能

这性质有啥用呢,这表明每次对于[l,r]的人,

可以去二分一个位置pos1,左端点>=pos1的[pos1,r]可以拓到l,

再二分一个位置pos2,右端点<=pos2的[l,pos2]可以拓到r,

对应产生的贡献,是拓之后-拓之前的差,认识是相互的,答案除以2即可

这个线段树维护最大最小值,维护区间和,然后线段树二分就可以解决

代码

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=5e5+10;
struct segtree1{
	int n;
	struct node{int l,r,c;ll v,mx;}e[N<<2];
	#define l(p) e[p].l
	#define r(p) e[p].r
	#define c(p) e[p].c
	#define v(p) e[p].v
	#define mx(p) e[p].mx
	void up(int p){v(p)=v(p<<1)+v(p<<1|1);mx(p)=max(mx(p<<1),mx(p<<1|1));}
	void bld(int p,int l,int r){
		l(p)=l;r(p)=r;c(p)=-1;
		if(l==r){v(p)=mx(p)=l;return;}
		int mid=l+r>>1;
		bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
		up(p);
	}
	void init(int _n){n=_n;bld(1,1,n);}
	void psd(int p){
		if(c(p)==-1)return;
		v(p<<1)=1ll*c(p)*(r(p<<1)-l(p<<1)+1);
		v(p<<1|1)=1ll*c(p)*(r(p<<1|1)-l(p<<1|1)+1);
		mx(p<<1)=c(p<<1)=c(p);
		mx(p<<1|1)=c(p<<1|1)=c(p);
		c(p)=-1;
	}
	void chg(int p,int ql,int qr,int v){
		if(ql<=l(p) && r(p)<=qr){
			c(p)=mx(p)=v;
			v(p)=1ll*v*(r(p)-l(p)+1);
			return;
		}
		psd(p);
		int mid=(l(p)+r(p))>>1;
		if(ql<=mid)chg(p<<1,ql,qr,v);
		if(qr>mid)chg(p<<1|1,ql,qr,v);
		up(p);
	}
	int ask(int p,int ql,int qr,int v){//[ql,qr]内lpos>=v的最小的pos 
		if(l(p)==r(p))return l(p);
		psd(p);
		int mid=(l(p)+r(p))>>1;
		if(ql<=mid && mx(p<<1)>=v)return ask(p<<1,ql,qr,v);
		else if(qr>mid && mx(p<<1|1)>=v)return ask(p<<1|1,ql,qr,v);
		return -1;	
	} 
	ll cnt(int p,int ql,int qr){
		if(ql<=l(p)&&r(p)<=qr)return v(p);
		psd(p);
		int mid=l(p)+r(p)>>1;ll res=0;
		if(ql<=mid)res+=cnt(p<<1,ql,qr);
		if(qr>mid)res+=cnt(p<<1|1,ql,qr);
		return res;
	}
}ltr;//维护左端点 
struct segtree2{
	int n;
	struct node{int l,r,c;ll v,mn;}e[N<<2];
	#define l(p) e[p].l
	#define r(p) e[p].r
	#define c(p) e[p].c
	#define v(p) e[p].v
	#define mn(p) e[p].mn
	void up(int p){v(p)=v(p<<1)+v(p<<1|1);mn(p)=min(mn(p<<1),mn(p<<1|1));}
	void bld(int p,int l,int r){
		l(p)=l;r(p)=r;c(p)=-1;
		if(l==r){v(p)=mn(p)=l;return;}
		int mid=l+r>>1;
		bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
		up(p);
	}
	void init(int _n){n=_n;bld(1,1,n);}
	void psd(int p){
		if(c(p)==-1)return;
		v(p<<1)=1ll*c(p)*(r(p<<1)-l(p<<1)+1);
		v(p<<1|1)=1ll*c(p)*(r(p<<1|1)-l(p<<1|1)+1);
		mn(p<<1)=c(p<<1)=c(p);
		mn(p<<1|1)=c(p<<1|1)=c(p);
		c(p)=-1;
	}
	void chg(int p,int ql,int qr,int v){
		if(ql<=l(p) && r(p)<=qr){
			c(p)=mn(p)=v;
			v(p)=1ll*v*(r(p)-l(p)+1);
			return;
		}
		psd(p);
		int mid=(l(p)+r(p))>>1;
		if(ql<=mid)chg(p<<1,ql,qr,v);
		if(qr>mid)chg(p<<1|1,ql,qr,v);
		up(p);
	}
	int ask(int p,int ql,int qr,int v){//[ql,qr]内rpos<=v的最大的pos 
		if(l(p)==r(p))return l(p);
		psd(p);
		int mid=(l(p)+r(p))>>1;
		if(qr>mid && mn(p<<1|1)<=v)return ask(p<<1|1,ql,qr,v);
		else if(ql<=mid && mn(p<<1)<=v)return ask(p<<1,ql,qr,v);
		return -1;	
	} 
	ll cnt(int p,int ql,int qr){
		if(ql<=l(p)&&r(p)<=qr)return v(p);
		psd(p);
		int mid=l(p)+r(p)>>1;ll res=0;
		if(ql<=mid)res+=cnt(p<<1,ql,qr);
		if(qr>mid)res+=cnt(p<<1|1,ql,qr);
		return res;
	}
}rtr;//维护右端点 
int n,m,l,r; 
int main(){
	while(~scanf("%d%d",&n,&m)){
		ltr.init(n);rtr.init(n);
		for(int i=1;i<=m;++i){
			scanf("%d%d",&l,&r);
			int pos1=ltr.ask(1,l,r,l);
			int pos2=rtr.ask(1,l,r,r);
			ll ans=0;
			if(~pos1){
				ans+=ltr.cnt(1,pos1,r)-1ll*(r-pos1+1)*l;
				ltr.chg(1,pos1,r,l);
			}
			if(~pos2){
				ans+=1ll*(pos2-l+1)*r-rtr.cnt(1,l,pos2);
				rtr.chg(1,l,pos2,r);
			}
			printf("%lld\n",ans/2);
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值