P5103 [JOI 2016 Final] 断层 题解

· · 题解

提高 T1 难度 远古 JOI 题。这东西能评黑???

干什么

有两个长度为 n 个序列 (x_1,x_2,\dots,x_n),(y_1,y_2,\dots,y_n),初始化 x_i=y_i=i。有 q 次操作,每次给你三个数 X,D,L,若 D=1 将所有满足 x_i\le Xiy_i2L,若 D=2 则将所有满足 y_i> Xix_i2L,求出最终的 x,y 序列。

怎么做

发现 x,y 两个序列任意时刻都是单调递增的,两种操作分别是前缀减和后缀加,二分出修改的区间然后树状数组维护即可,时间复杂度 O(q\log^2 n)

代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 200003
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
struct node{
    ll x,d,l;
}a[mxn];
int n,q;
struct tree{
    ll c[mxn];
    void add(int x,int y){for(;x<=n;x+=x&-x)c[x]+=y;}
    ll ask(int x){
        ll s=0;
        for(;x;x-=x&-x)s+=c[x];
        return s;
    }
}c1,c2;
signed main(){
    scanf("%d%d",&n,&q);
    rep(i,1,q)scanf("%lld%lld%lld",&a[i].x,&a[i].d,&a[i].l),a[i].l<<=1;
    rep(i,1,n)c1.add(i,1),c2.add(i,1);
    drep(j,q,1){
        if(a[j].d==1){
            int l=1,r=n;
            while(l<r){
                int mid=(l+r+1)>>1;
                if(c1.ask(mid)<=a[j].x)l=mid;
                else r=mid-1;
            }
            if(c1.ask(l)<=a[j].x)c2.add(1,-a[j].l),c2.add(l+1,a[j].l);
        }else{
            int l=1,r=n;
            while(l<r){
                int mid=(l+r)>>1;
                if(c2.ask(mid)>a[j].x)r=mid;
                else l=mid+1;
            }
            if(c2.ask(l)>a[j].x)c1.add(l,a[j].l);
        }
    }
    rep(i,1,n)printf("%lld\n",(c1.ask(i)-c2.ask(i))>>1);
    return 0;
}