洛谷P4243/bzoj1558 解題報告(線段樹維護差分+爆炸噁心的合併)
阿新 • • 發佈:2018-12-08
首先感謝這篇題解,是思路來源
看到等差數列,就會想到差分,又有區間加,很容易想到線段樹維護差分。再注意點細節,於\(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); } } }