【NOIP2015模拟11.2晚】我的天

Description

有n个人排成一列。给出m次操作,每次操作让区间l~r的人互相认识。求每次操作后新增了多少对认识的人。
n,m<=300000

Solution

很显然,每个人认识的人都是一个区间内的。为了避免算重复,我们可以只计算他左边和他认识的人。设left[i]表示left[i]~i-1都和i认识,那么答案就为 ni=1ileft[i] 。然后每次询问相当于把left[l]~left[r]min上一个l。
可以用线段树维护这个过程。
怎么区间取min?
我们发现left具有不减性,所以我们只需要找出第一个大于l的地方,然后区间赋值就可以了。

Code

#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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值