洛谷P4243/bzoj1558 [JSOI2009]等差数列(线段树维护差分+爆炸恶心的合并)

题面

首先感谢这篇题解,是思路来源


看到等差数列,就会想到差分,又有区间加,很容易想到线段树维护差分。再注意点细节,\(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 
#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

转载于:https://www.cnblogs.com/PsychicBoom/p/10086878.html

你可能感兴趣的:(洛谷P4243/bzoj1558 [JSOI2009]等差数列(线段树维护差分+爆炸恶心的合并))