我的天

题目大意

每次选择一次区间让区间内的人人两两认识(已经认识过的不会重复认识)
并在每次操作后输出增加了多少对认识的人。

抖机灵

我们设left[i]表示[left[i],i-1]的人都与i认识。
我们知道我们只需要求出每次操作后一共有多少对人认识,然后作差输出即可。
而且ans=ni=1ileft[i]
如果每次选择了[l,r],那么相当于让区间[l,r]的left值对l取min。
于是我们描述我们的任务:
1、支持区间取min。
2、支持求和。
segment tree beats?!!

单调性

然而这题存在特殊性。
注意到单调性:left[i-1]<=left[i]。
即i认识的i-1一定认识!
那么就好办了,每次操作找到区间[l,r]中最小的k满足left[k]>=l,然后相当于区间set。
至于如何找到这个k,我们只需区间保存最大值,然后利用线段树特性找就好了。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=300000+10;
int mx[maxn*5],set[maxn*5];
bool bz[maxn*5];
ll sum[maxn*5];
int i,j,k,l,r,t,n,m;
ll all,ans;
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void mark(int p,int l,int r,int v){
    mx[p]=v;
    sum[p]=(ll)(r-l+1)*v;
    bz[p]=1;
    set[p]=v;
}
void down(int p,int l,int r){
    int mid=(l+r)/2;
    if (bz[p]){
        mark(p*2,l,mid,set[p]);
        mark(p*2+1,mid+1,r,set[p]);
        set[p]=bz[p]=0;
    }
}
void change(int p,int l,int r,int a,int b,int v){
    if (l==a&&r==b){
        mark(p,l,r,v);
        return;
    }
    down(p,l,r);
    int mid=(l+r)/2;
    if (b<=mid) change(p*2,l,mid,a,b,v);
    else if (a>mid) change(p*2+1,mid+1,r,a,b,v);
    else change(p*2,l,mid,a,mid,v),change(p*2+1,mid+1,r,mid+1,b,v);
    mx[p]=max(mx[p*2],mx[p*2+1]);
    sum[p]=sum[p*2]+sum[p*2+1];
}
int find(int p,int l,int r,int a,int b,int v){
    if (l==r){
        if (mx[p]<v) return l+1;else return l;
    }
    down(p,l,r);
    int mid=(l+r)/2;
    if (l==a&&r==b){
        if (mx[p]<v) return r+1;
        if (mx[p*2]>=v) return find(p*2,l,mid,a,mid,v);
        else return find(p*2+1,mid+1,r,mid+1,b,v);
    }
    if (b<=mid) return find(p*2,l,mid,a,b,v);
    else if (a>mid) return find(p*2+1,mid+1,r,a,b,v);
    else{
        int t=find(p*2,l,mid,a,mid,v);
        if (t<=mid) return t;else return find(p*2+1,mid+1,r,mid+1,b,v);
    }
}
int main(){
    freopen("ohmygod.in","r",stdin);freopen("ohmygod.out","w",stdout);
    scanf("%d%d",&n,&m);
    all=(ll)n*(n+1)/2;
    fo(i,1,n) change(1,1,n,i,i,i);
    fo(i,1,m){
        scanf("%d%d",&j,&k);
        t=find(1,1,n,j,k,j);
        if (t<=k) change(1,1,n,t,k,j);
        printf("%lld\n",all-sum[1]-ans);
        ans=all-sum[1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值