吉司机线段树2题

51nod上膜拜了吉司机线段树的直播,写了两个题,分别是hdu5306以及bzoj4355,由于代码能力过于垃圾,每个题都写了两天。
主要思路是用cut对线段树的更新做剪枝,用check控制暴力更新的条件。大概就是区间覆盖或者类似的操作会把区间变得越来越相同,对于把区间变得相同(势能降低?)的操作,完全可以暴力更新,而没有降低势能的操作基本上可以打标记。

整个框架如下:

void update(int u, int ql, int qr, int c, int l, int r){
    if(r<ql||qr<l||cut())return;
    if(ql<=l&&r<=qr&&check()){
        putlazy(u,c);
        return;
    }
    int mid=(l+r)/2;
    pushdown(u);
    update(2*u, ql, qr, c, l, mid);
    update(2*u+1, ql, qr, c, mid+1, r);
    pushup(u);
}

对于hdu5306这个题,考虑区间与t取min这个操作,有如下几种情况:
case 1:t大于最大值,此时区间不变;
case 2:t小于严格次大值,此时至少把最大值和次大值变得相同,即使得区间变得相同,允许暴力更新;
case 3:t大于严格次大值,小于最大值,这里可以打懒标记。

考虑查询,只需维护最大值,最大值个数,严格次大值即可。

//吉司机宇宙线段树之王!
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000005;
typedef long long ll;

int mx[maxn<<2];
int cnt[maxn<<2];
int se[maxn<<2];
int lazy[maxn<<2];
ll sum[maxn<<2];
int a[maxn];
int n, m;

void putlazy(int u, int t){
    sum[u]-=1LL*cnt[u]*(mx[u]-t);
    mx[u]=t;
    lazy[u]=t;
}

void pushdown(int u){
    if(lazy[u]==-1)return;
    if(mx[2*u]>lazy[u]){
        sum[2*u]-=1LL*cnt[2*u]*(mx[2*u]-lazy[u]);
        mx[2*u]=lazy[u];
        lazy[2*u]=lazy[u];
    }
    if(mx[2*u+1]>lazy[u]){
        sum[2*u+1]-=1LL*cnt[2*u+1]*(mx[2*u+1]-lazy[u]);
        mx[2*u+1]=lazy[u];
        lazy[2*u+1]=lazy[u];

    }
    lazy[u]=-1;
}

void pushup(int u){
    if(mx[2*u]==mx[2*u+1]){
        mx[u]=mx[2*u];
        cnt[u]=cnt[2*u]+cnt[2*u+1];
        se[u]=max(se[2*u], se[2*u+1]);
        sum[u]=sum[2*u]+sum[2*u+1];
    }
    else if(mx[2*u]>mx[2*u+1]){
        mx[u]=mx[2*u];
        cnt[u]=cnt[2*u];
        se[u]=max(se[2*u], mx[2*u+1]);
        sum[u]=sum[2*u]+sum[2*u+1];
    }
    else {
        mx[u]=mx[2*u+1];
        cnt[u]=cnt[2*u+1];
        se[u]=max(mx[2*u], se[2*u+1]);
        sum[u]=sum[2*u]+sum[2*u+1];
    }
}

void build(int u, int l, int r){
    lazy[u]=-1;
    if(l==r){
        mx[u]=sum[u]=a[l];
        cnt[u]=1;
        se[u]=-1;
        return;
    }
    int mid=l+r>>1;
    build(2*u, l, mid);
    build(2*u+1, mid+1, r);
    pushup(u);
}

void update(int u, int ql, int qr, int t, int l, int r){
    if(ql>r||qr<l||mx[u]<=t)return;
    if(ql<=l&&r<=qr&&se[u]<t){
        putlazy(u, t);
        return;
    }
    pushdown(u);
    int mid=l+r>>1;
    update(2*u, ql, qr, t, l, mid);
    update(2*u+1, ql, qr, t, mid+1, r);
    pushup(u);
}

int getmx(int u, int ql, int qr, int l, int r){
    if(ql>r||qr<l)return 0;
    if(ql<=l&&r<=qr)return mx[u];
    pushdown(u);
    int mid=l+r>>1;
    int ans=0;
    ans=max(ans, getmx(2*u, ql, qr, l, mid));
    ans=max(ans, getmx(2*u+1, ql, qr, mid+1, r));
    return ans;
}

ll getsum(int u, int ql, int qr, int l, int r){
    if(ql>r||qr<l)return 0;
    if(ql<=l&&r<=qr)return sum[u];
    pushdown(u);
    int mid=l+r>>1;
    ll ans=0;
    ans+=getsum(2*u, ql, qr, l, mid);
    ans+=getsum(2*u+1, ql, qr, mid+1, r);
    return ans;
}

int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        for(int i=1;i<=n;i++)scanf("%d", &a[i]);
        build(1, 1, n);
        for(int i=1;i<=m;i++){
            int tag;
            scanf("%d", &tag);
            if(tag==0){
                int x, y, t;
                scanf("%d%d%d", &x, &y, &t);
                update(1, x, y, t, 1, n);
            }
            else if(tag==1){
                int x, y;
                scanf("%d%d", &x, &y);
                printf("%d\n", getmx(1, x, y, 1, n));
            }
            else {
                int x, y;
                scanf("%d%d", &x, &y);
                printf("%lld\n", getsum(1, x, y, 1, n));
            }
        }
    }
}

而对于bzoj4355来说,基本上是类似的。需要维护区间最小值,区间最小值个数,严格次小值。
对于操作1区间赋值,如果区间不相同,那肯定是把区间变得相同,所以可以暴力更新,而当区间全部相同,区间赋值可以看成区间增,可以打区间增的标记。
对于操作2,可以看做先区间增,再区间与0取max。
类似上一题的操作,唯一区别在于要维护两个懒标记,一个是区间增,另一个是区间取max。

//吉司机线段树
//牛逼啊
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=300005;

int n, m;
ll mn[maxn<<2];
int cnt[maxn<<2];
ll se[maxn<<2];
ll lzcut[maxn<<2];
ll lzadd[maxn<<2];
int a[maxn];

void putcut(int u, ll c){
    mn[u]=c;
    lzcut[u]=c;
}

void putadd(int u, ll c){
    lzadd[u]+=c;
    if(lzcut[u]!=-1)lzcut[u]+=c;
    mn[u]+=c;
    if(se[u]!=-1)se[u]+=c;
}

void pushdown(int u){
    if(lzadd[u]){
        putadd(2*u, lzadd[u]);
        putadd(2*u+1, lzadd[u]);
        lzadd[u]=0;
    }
    if(lzcut[u]!=-1){
        if(mn[2*u]<lzcut[u]){
            mn[2*u]=lzcut[u];
            lzcut[2*u]=lzcut[u];
        }
        if(mn[2*u+1]<lzcut[u]){
            mn[2*u+1]=lzcut[u];
            lzcut[2*u+1]=lzcut[u];
        }
        lzcut[u]=-1;
    }
}

void pushup(int u){
    if(mn[2*u]==mn[2*u+1]){
        mn[u]=mn[2*u];
        cnt[u]=cnt[2*u]+cnt[2*u+1];
        if(se[2*u]==-1&&se[2*u+1]==-1)se[u]=-1;
        else if(se[2*u]==-1)se[u]=se[2*u+1];
        else if(se[2*u+1]==-1)se[u]=se[2*u];
        else se[u]=min(se[2*u], se[2*u+1]);
    }
    else if(mn[2*u]<mn[2*u+1]){
        mn[u]=mn[2*u];
        cnt[u]=cnt[2*u];
        if(se[2*u]==-1){
            se[u]=mn[2*u+1];
        }
        else {
            se[u]=min(mn[2*u+1], se[2*u]);
        }
    }
    else {
        mn[u]=mn[2*u+1];
        cnt[u]=cnt[2*u+1];
        if(se[2*u+1]==-1){
            se[u]=mn[2*u];
        }
        else {
            se[u]=min(mn[2*u], se[2*u+1]);
        }
    }
}

void build(int u, int l, int r){
    lzcut[u]=-1;
    lzadd[u]=0;
    if(l==r){
        mn[u]=a[l];
        cnt[u]=1;
        se[u]=-1;
        return;
    }
    int mid=(l+r)/2;
    build(2*u, l, mid);
    build(2*u+1, mid+1, r);
    pushup(u);
}

void cover(int u, int ql, int qr, int c, int l, int r){
    if(r<ql||qr<l)return;
    if(ql<=l&&r<=qr&&se[u]==-1){
        putadd(u, (ll)c-mn[u]);
        return;
    }
    int mid=(l+r)/2;
    pushdown(u);
    cover(2*u, ql, qr, c, l, mid);
    cover(2*u+1, ql, qr, c, mid+1, r);
    pushup(u);
}

void add(int u, int ql, int qr, int c, int l, int r){
    if(r<ql||qr<l||(se[u]==-1&&mn[u]==0&&c<=0))return;
    if(ql<=l&&r<=qr){
        if(mn[u]+c>=0){
            putadd(u, c);
            return;
        }
        else if(se[u]==-1||se[u]+c>0){
            putadd(u, c);
            putcut(u, 0);
            return;
        }
    }
    int mid=(l+r)/2;
    pushdown(u);
    add(2*u, ql, qr, c, l, mid);
    add(2*u+1, ql, qr, c, mid+1, r);
    pushup(u);
}

int query(int u, int ql, int qr, int l, int r){
    if(r<ql||qr<l)return 0;
    if(ql<=l&&r<=qr){
        if(mn[u]==0)return cnt[u];
        return 0;
    }
    int mid=(l+r)/2;
    int ret=0;
    pushdown(u);
    ret+=query(2*u, ql, qr, l, mid);
    ret+=query(2*u+1, ql, qr, mid+1, r);
    return ret;
}


int main(){
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++){
        scanf("%d", &a[i]);
    }
    build(1, 1, n);
    for(int i=1;i<=m;i++){
        int tag;
        scanf("%d", &tag);
        if(tag==1){
            int l, r, c;
            scanf("%d%d%d", &l, &r, &c);
            cover(1, l, r, c, 1, n);
        }
        else if(tag==2){
            int l, r, c;
            scanf("%d%d%d", &l, &r, &c);
            add(1, l, r, c, 1, n);
        }
        else {
            int l, r;
            scanf("%d%d", &l, &r);
            printf("%d\n", query(1, l, r, 1, n));
        }
    }
}

猜你喜欢

转载自blog.csdn.net/mrbird_to_fly/article/details/77141881