1. 程式人生 > >洛谷P4243/bzoj1558 解題報告(線段樹維護差分+爆炸噁心的合併)

洛谷P4243/bzoj1558 解題報告(線段樹維護差分+爆炸噁心的合併)

題面

首先感謝這篇題解,是思路來源


看到等差數列,就會想到差分,又有區間加,很容易想到線段樹維護差分。再注意點細節,於\(A\)操作完美解決
然後就是爆炸噁心的\(B\)操作,之前看一堆題解的解釋都不怎麼明白,就自己腦補+看上面那篇題解亂搞出了個相對合理點的解釋……
\(0/1/2/3\)分別表示一個差分割槽間統計答案時,是否跨越原區間左右端點。\(s[0/1/2/3]\)分別表示每個狀態的最少可以劃分出來的等差數列個數。
合併方式如下:

/*定義差分b[i]=a[i+1]-a[i]
假設要查詢區間[S,T],對應到差分陣列實際查詢的是區間[S,T-1]
所以一個節點如果在差分陣列上維護的區間是[a,b],在原陣列上維護的實際上是區間[a,b+1]*/

//s[0]表示原陣列中區間為(S,T)的答案
//s[1]表示原陣列中區間為(S,T]的答案
//s[2]表示原陣列中區間為[S,T)的答案
//s[3]表示原陣列中區間為[S,T]的答案

s[0]= min(s[1]+ s[2]- ( vr==Y.vl), min(s[0]+ s[2], s[1]+s[0] ) );

s[1]= min(s[1]+ Y.s[3]- ( vr==Y.vl), min(s[1]+ s[1], s[0]+s[3] ) );

s[2]= min(s[3]+ s[2]- ( vr==Y.vl), min(s[2]+ s[2], s[3]+s[0] ) );

s[3]= min(s[3]+ s[3]- ( vr==Y.vl), min(s[3]+ s[1], s[2]+s[3] ) );

輸出時直接輸出\(s[3]\)就OK了

\(p.s.\) \(n==1\)時不用建樹,否則\(RE\)

程式碼:

#include <bits/stdc++.h>
#define N 100005
using namespace std;

int a[N],n;

#define bas int l,int r,int o
#define lson l,mid,o<<1
#define rson mid+1,r,o<<1|1

struct node{
    int s[4];
    long long vl,vr;
    node operator +(const node &Y)const{
        node Z;
        Z.vl=vl,Z.vr=Y.vr; 
        Z.s[0]= min(s[1]+ Y.s[2]- ( vr==Y.vl), min(s[0]+ Y.s[2], s[1]+Y.s[0] ) );
        Z.s[1]= min(s[1]+ Y.s[3]- ( vr==Y.vl), min(s[1]+ Y.s[1], s[0]+Y.s[3] ) );
        Z.s[2]= min(s[3]+ Y.s[2]- ( vr==Y.vl), min(s[2]+ Y.s[2], s[3]+Y.s[0] ) );
        Z.s[3]= min(s[3]+ Y.s[3]- ( vr==Y.vl), min(s[3]+ Y.s[1], s[2]+Y.s[3] ) );
        return Z; 
    }
};
struct qwq{
    long long tag[N<<2];
    node t[N<<2]; 
    void pushdown(int o){
        t[o<<1].vl+= tag[o], t[o<<1].vr+= tag[o];
        t[o<<1|1].vl+= tag[o], t[o<<1|1].vr+= tag[o];
        tag[o<<1]+= tag[o], tag[o<<1|1]+= tag[o]; 
        tag[o]=0;
    }
    void build(bas){
        tag[o]=0;
        if(l==r){
            t[o].vl= t[o].vr= a[l];
            t[o].s[1]= t[o].s[2]= t[o].s[3]= 1, t[o].s[0]=0;//兩個數時和一個數時等差數列數量都是1
            return;
        }
        int mid=(l+r)>>1;
        build(lson),build(rson);
        t[o]= t[o<<1]+ t[o<<1|1];
    }
    void update(bas,int L,int R,long long x){
        if(L<=l && r<=R){
            t[o].vl+= x, t[o].vr+= x;
            tag[o]+= x;
            return;
        }
        if(tag[o]) pushdown(o);
        int mid= (l+r)>>1;
        if(L<= mid) update(lson,L,R,x);
        if(R> mid) update(rson,L,R,x);
        t[o]= t[o<<1]+ t[o<<1|1];        
    }
    node query(bas,int L,int R){ 
        if(L<= l && r<= R) return t[o];
        if(tag[o]) pushdown(o);
        int mid= (l+r)>>1;
        if(R<=mid) return query(lson,L,R);
        else{
            if(L>mid) return query(rson,L,R);
            else return query(lson,L,R)+query(rson,L,R);
        }   
    }   
}T;

int main(){
    int q,s,t,c,d,ans,i;
    char op;
    scanf("%d",&n);
    for(i=1;i<=n;++i) scanf("%d",&a[i]);
    for(i=1;i<n;++i) a[i]=a[i+1]-a[i];
    if(n!=1) T.build(1,n-1,1);
    scanf("%d",&q);
    while(q--){
        scanf("\n%c",&op);
        if(op=='A'){
            scanf("%d%d%d%d",&s,&t,&c,&d);
            if(s!=1) T.update(1,n-1,1,s-1,s-1,c);
            if(t!=n) T.update(1,n-1,1,t,t,1ll*d*s-1ll*d*t-c); 
            if(s!=t) T.update(1,n-1,1,s,t-1,d);
        } else{
            scanf("%d%d",&s,&t);
            if(t- s+ 1==1) ans=1; 
            else{
                node res=T.query(1,n-1,1,s,t-1);
                ans=min((t-s+2)/2,res.s[3]);
            }
            printf("%d\n",ans); 
        }
    }
}