有n个人排成一列。给出m次操作,每次操作让区间l~r的人互相认识。求每次操作后新增了多少对认识的人。
n,m<=300000
很显然,每个人认识的人都是一个区间内的。为了避免算重复,我们可以只计算他左边和他认识的人。设left[i]表示left[i]~i-1都和i认识,那么答案就为 ∑ni=1i−left[i] 。然后每次询问相当于把left[l]~left[r]min上一个l。
可以用线段树维护这个过程。
怎么区间取min?
我们发现left具有不减性,所以我们只需要找出第一个大于l的地方,然后区间赋值就可以了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 300005
#define ll long long
using namespace std;
struct note{
int mx;ll sum;
}t[N*5];
int n,m,x,y,lazy[N*5];
ll sum,ans;
void back(int v,int x,int y,int z) {
lazy[v]=z;t[v].mx=z;t[v].sum=(ll)(y-x+1)*z;
}
void down(int v,int l,int r) {
if (lazy[v]) {
int m=(l+r)/2;
back(v*2,l,m,lazy[v]);back(v*2+1,m+1,r,lazy[v]);
lazy[v]=0;
}
}
void change(int v,int l,int r,int x,int y,int z) {
if (l==x&&r==y) {
back(v,x,y,z);return;
}
int m=(l+r)/2;down(v,l,r);
if (y<=m) change(v*2,l,m,x,y,z);
else if (x>m) change(v*2+1,m+1,r,x,y,z);
else change(v*2,l,m,x,m,z),change(v*2+1,m+1,r,m+1,y,z);
t[v].mx=max(t[v*2].mx,t[v*2+1].mx);
t[v].sum=t[v*2].sum+t[v*2+1].sum;
}
int find(int v,int l,int r,int x) {
if (l==r) return l;
int m=(l+r)/2;
if (t[v*2].mx>x) find(v*2,l,m,x);
else find(v*2+1,m+1,r,x);
}
int main() {
freopen("ohmygod.in","r",stdin);
freopen("ohmygod.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n) change(1,1,n,i,i,i);
fo(i,1,m) {
scanf("%d%d",&x,&y);sum=ans;
if (t[1].mx>x) {
int l=find(1,1,n,x);
if (l<=y) change(1,1,n,l,y,x);
}
ans=(ll)n*(n+1)/2-t[1].sum;
printf("%lld\n",ans-sum);
}
}