【 bzoj 4355 】 Play with sequence - 线段树乱搞

  先讲个故事。。。
  据说某一天,claris扔了一道题到某群里面然后引起了不大的讨论~然后好学向上的whx同学发现了这题。。。聪明的whx想了很久。。。然后!whx发现看不懂claris给的暴力的证明。。。于是去找了吉司机。。。吉司机若有所思。。。再后来跟whx说:这是对的。。。然而whx同学并没有写。。。最后就弃坑啦。。。
  第二年,whx来到WC2016的会场上,发现:诶,吉司机的线段树怎么这么。。。眼熟?
  自行脑补
  咳咳,回到正题。
  去了WC2016的都知道这类取max的题的做法。
  容易发现线段树直接做不太好做,尝试深入发掘一下线段树的性质。
  假设我们需要对 i[l,r] 进行 Aimax{Ai,x} 的操作,考虑用线段树瞎搞。对每个节点 u 维护最小值 mn[u] 和次小值 se[u] ,那么我们可以对 v 进行分类讨论。
  当 v<=mn[u] 时不鸟它。当 mn[u]<v<se[u] 时我们可以打一个“最小值的cover标记”,把最小值都给设成 v 。当 v>=se[u] 的时候继续暴力修改下去直到当前区间满足条件。jry和lzz的营员交流证明了这个做法总复杂度是 O((m+n)logn) 的。然而总是有一种这玩意是两个log的错觉
  于是到这道题里面就差不多地搞了。首先我们可以把操作2归约成一个最小值增加操作和区间增加操作。然后操作1可以变成 max{Aiinf,0} ,再区间加上 C 。注意这个做法不小心的话会爆掉long long。还有一种做法是增加一个区间cover标记,这样常数会小一些。操作3直接搞就好了。
  注意代码实现的时候有不少的坑点,比如最小值增量标记必须在 mn[u]<v<se[u] 的时候打,下传标记的时候要注意判一下最小值是否相等。
  复杂度 O((n+m)logn)
  
  

#include 
using namespace std;
#define rep(i,a,b) for (int i = a , _ = b;i <= _;i ++)
#define maxn 300007

inline int rd() {
    char c = getchar();
    while (!isdigit(c) && c != '-') c = getchar() ; int x = 0 , f = 1;
    if (c == '-') f = -1 ; else x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x * f;
}

const int maxs = 1500007;

typedef long long ll;

typedef ll  seg[maxs];
typedef int arr[maxn];

arr a;
int n , m;

#define lc (u << 1)
#define rc (u << 1 | 1)
#define T int u = 1 , int l = 1 , int r = n
#define U u , l , r
#define L lc , l , m
#define R rc , m + 1 , r
#define current ql <= l && r <= qr

const ll inf = 1000000000000ll;

static int DBG_CLOCK = 0;

#define ASS //printf("current time %d\n" , ++ DBG_CLOCK),assert(sum[u] > 0);

struct SegTree {
    ll tag[maxs][3];
    seg mn , se , sum;

    int ql , qr;
    ll v;

    //tag0 : cover ; tag1 : add ; tag3 : delta of minimum

    inline void mt(int u) {
        mn[u] = min(mn[lc] , mn[rc]);
        if (mn[lc] == mn[rc])
            sum[u] = sum[lc] + sum[rc] , se[u] = min(se[lc] , se[rc]);
        if (mn[lc] != mn[rc]) {
            if (mn[lc] > mn[rc])
                se[u] = min(mn[lc] , se[rc]) , sum[u] = sum[rc];
            else
                se[u] = min(mn[rc] , se[lc]) , sum[u] = sum[lc];
        }
        ASS
    }

    inline void mark_cover(ll v , T) {
        tag[u][0] = v;
        mn[u] = v , se[u] = inf;
        tag[u][1] = tag[u][2] = 0;
        sum[u] = r - l + 1;
//      printf("%d %d %d\n" , u , l , r);
        ASS
    }

    inline void mark_add(ll v , T) {
        tag[u][1] += v;
        mn[u] -= v;
        if (se[u] != inf) se[u] -= v;
    }

    inline void mark_delta(ll v , T) {
        tag[u][2] += v;
        mn[u] -= v;
    }

    inline bool mark_modi(T) {
        if (v <= mn[u]) {
            mark_add(v , U);
        } else if (mn[u] < v && v < se[u]) { // v !!!!!= se[u]
            mark_delta(mn[u] - v , U);
            mark_add(v , U);
        } else { // v >= se[u] , modify forcely
            if (l == r)
                return (mn[u] = 0 , se[u] = inf , sum[u] = 1) , 1;
            else
                return 0;
        }
        return 1;
    }

    inline void push(T) {
        int m = (l + r) >> 1;
        if (tag[u][0] != -1) {
            mark_cover(tag[u][0] , L) , mark_cover(tag[u][0] , R);
            tag[u][0] = -1;
        }
        if (tag[u][1]) {
            mark_add(tag[u][1] , L) , mark_add(tag[u][1] , R);
            tag[u][1] = 0;
        }
        if (tag[u][2]) {
            if (mn[lc] - tag[u][2] == mn[u]) mark_delta(tag[u][2] , L);
            if (mn[rc] - tag[u][2] == mn[u]) mark_delta(tag[u][2] , R);
            tag[u][2] = 0;
        }
    }

    void build(T) {
        tag[u][0] = -1;
        if (l == r) {
            mn[u] = a[l] , se[u] = inf;
            sum[u] = 1;
            return;
        }
        int m = (l + r) >> 1;
        build(L) , build(R);
        mt(u);
    }

    int que(T) {
        if (l != r)
            push(U);
        if (current)
            return mn[u] == 0 ? sum[u] : 0;
        int m = (l + r) >> 1 , t = 0;
        if (ql <= m) t += que(L);
        if (qr >  m) t += que(R);
        return t;
    }

    void cover(T) {
        if (l != r)
            push(U);
        if (current)
            { mark_cover(v , U) ; return ; }
        int m = (l + r) >> 1;
        if (ql <= m) cover(L);
        if (qr >  m) cover(R);
        mt(u);
    }

    void modi(T) {
        if (l != r)
            push(U);
        if (current) 
            if (mark_modi(U))
                return;
        int m = (l + r) >> 1;
        if (ql <= m) modi(L);
        if (qr >  m) modi(R);
        mt(u);
    }

    inline int Q(int l , int r) {
        ql = l , qr = r;
        return que();
    }

    inline void S(int l , int r , ll w) {
        ql = l , qr = r , v = w;
        cover();
    }

    inline void M(int l , int r , ll w) {
        ql = l , qr = r , v = w;
        modi();
    }

    inline void B() {
        build();
    }
}num;

void input() {
    n = rd() , m = rd();
    rep (i , 1 , n) a[i] = rd();
    num.B();
}

void SetVal(int l , int r) {
    int v = rd();
//  num.M(l , r , -inf + 1);
//  num.M(l , r , v);
    num.S(l , r , v);
}

void Change(int l , int r) {
    int v = - rd();
    num.M(l , r , v);
}

void query(int l , int r) {
    int ans = num.Q(l , r);
    printf("%d\n" , ans);
}

void solve() {
    rep (i , 1 , m) {
        int t = rd() , l = rd() , r = rd();
        if (t == 1)
            SetVal(l , r);
        else if (t == 2)
            Change(l , r);
        else
            query(l , r);
    }
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
        freopen("data.out" , "w" , stdout);
    #endif
    input();
    solve();
    return 0;
}

你可能感兴趣的:(线段树)