题目
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;
}