[JSOI2009]等差數列【差分+線段樹】
作為水到一等的弱省OI蒟蒻,甚至以為自己能聽懂SDWC的省選班。去了以後發現除了搜尋、仰望大佬和看妹子(還真有妹子很不錯)以外啥都不會…每天都是聽一小時之後棄療。
然而唯一有基礎的線段樹…也是一上來“大家都會寫線段樹吧”然後丟一道黑題(也就是這道)。同伴在身邊大呼簡單:“也就差分比較難想。”於是當天晚自習,我們點開了那道題…
寫完統計差分陣列中連續相等數的長度獲得了一堆Wonderful Answer後…發現正確答案比我們的輸出小一半後百思不得其解…點開題解,發現要維護什麼零散數列長度…同伴果斷放棄,我也知道考場上不可能有時間給你看題解,不過面對自己百餘行程式碼不忍棄之…然後懟了一週,終於a掉了。
唉,手殘毀一生,特此留念。
正文也沒啥可以寫的,各路大佬寫得都很透徹,我的碼力不足,常數巨大,那我總結一下看題解得到的經驗。
1.各路大佬都喜歡快讀,看來這種水平的題目,刷題者已經習慣快讀了;
2.當線段樹向上合併操作比較麻煩的時候,可以線上段樹節點的結構體中過載加號(我在這道題沒寫,導致pushup和query的時候寫了兩遍…);
3.差分陣列中第一項為原陣列第二項與第一項之差,以此類推,所以差分陣列中只有n-1個元素;
4.注意邊界問題的處理,區間加的時候看看邊界,比如第一項沒有與前一項的差什麼的;
5.千萬不能手殘!!!!!!!!!!!!!
#include<cstdio>
#include<algorithm>
#define lll long long
const lll maxn=100002;
lll tt[maxn],a[maxn],n,m,x,y,s,t,i;
char ch[3];
struct node{lll l,r,t,ll,rr,len;};
struct segtree{
#define lson (o<<1)
#define rson (o<<1|1)
#define mid ((l+r)>>1)
lll addv[maxn<<2],vall[maxn<<2 ],valr[maxn<<2],sumt[maxn<<2],suml[maxn<<2],sumr[maxn<<2],lenv[maxn<<2];
inline void pushup(lll o){
lll lenl=lenv[lson],lenr=lenv[rson];
lenv[o]=lenl+lenr;//此處手殘多處,有很多細節
suml[o]=sumr[o]=0;
vall[o]=vall[lson];valr[o]=valr[rson];sumt[o]=sumt[lson]+sumt[rson];
if(suml[lson]==lenl&&sumr[rson]==lenr){
if(vall[rson]==valr[lson]){sumt[o]=1;suml[o]=lenl-1;sumr[o]=lenr-1;}
else{sumt[o]=0;suml[o]=sumr[o]=lenv[o];}
return;
}
if(suml[lson]==lenl&&sumr[rson]!=lenr){
sumr[o]=sumr[rson];
if(vall[rson]==valr[lson]){suml[o]=lenl-1;if(suml[rson]>0)sumt[o]+=(suml[rson]-1)/2+1;}//此處手殘打錯一個字元,wa一半也是很絕望(資料也是挺水...能讓我過一半)
else suml[o]=suml[lson]+suml[rson];
return;
}
if(sumr[rson]==lenr&&sumr[lson]!=lenl){
suml[o]=suml[lson];
if(vall[rson]==valr[lson]){sumr[o]=lenr-1;if(sumr[lson]>0)sumt[o]+=(sumr[lson]-1)/2+1;}
else sumr[o]=sumr[lson]+sumr[rson];
return;
}
suml[o]=suml[lson];sumr[o]=sumr[rson];
if(suml[rson]==0&&sumr[lson]==0){
if(vall[rson]==valr[lson])sumt[o]--;
return;
}
if(sumr[lson]==0&&suml[rson]!=0){
if(vall[rson]==valr[lson])sumt[o]+=(suml[rson]-1)/2;
else sumt[o]+=suml[rson]/2;
return;
}
if(sumr[lson]!=0&&suml[rson]==0){
if(vall[rson]==valr[lson])sumt[o]+=(sumr[lson]-1)/2;
else sumt[o]+=sumr[lson]/2;
return;
}
if(vall[rson]==valr[lson])sumt[o]+=std::min((suml[rson]+sumr[lson])/2,(suml[rson]-1)/2+(sumr[lson]-1)/2+1);
else sumt[o]+=(suml[rson]+sumr[lson])/2;
}
inline void pushdown(lll o){
addv[lson]+=addv[o];addv[rson]+=addv[o];
vall[lson]+=addv[o];vall[rson]+=addv[o];
valr[lson]+=addv[o];valr[rson]+=addv[o];
addv[o]=0;
}
inline void build(lll o,lll l,lll r){
if(l==r){vall[o]=valr[o]=a[l];suml[o]=sumr[o]=lenv[o]=1;sumt[o]=addv[o]=0;return;}
build(lson,l,mid);
build(rson,mid+1,r);
pushup(o);
}
inline node querysum(lll o,lll l,lll r,lll ql,lll qr){
if(ql<=l&&qr>=r)return (node){vall[o],valr[o],sumt[o],suml[o],sumr[o],lenv[o]};
pushdown(o);
if(qr<=mid)return querysum(lson,l,mid,ql,qr);
else if(ql>mid)return querysum(rson,mid+1,r,ql,qr);
else{
node ans,ll,rr;//這裡又要打一遍,以後注意過載加號
ll=querysum(lson,l,mid,ql,qr);
rr=querysum(rson,mid+1,r,ql,qr);
lll lenl=ll.len,lenr=rr.len;
ans.ll=ans.rr=0;
ans.l=ll.l;ans.r=rr.r;ans.t=ll.t+rr.t;ans.len=ll.len+rr.len;
if(ll.ll==lenl&&rr.rr==lenr){
if(rr.l==ll.r){ans.t=1;ans.ll=lenl-1;ans.rr=lenr-1;}
else{ans.t=0;ans.ll=ans.rr=ans.len;}
return ans;
}
if(ll.ll==lenl&&rr.rr!=lenr){
ans.rr=rr.rr;
if(rr.l==ll.r){ans.ll=lenl-1;if(rr.ll>0)ans.t+=(rr.ll-1)/2+1;}
else ans.ll=ll.ll+rr.ll;
return ans;
}
if(rr.rr==lenr&&ll.rr!=lenl){
ans.ll=ll.ll;
if(rr.l==ll.r){ans.rr=lenr-1;if(ll.rr>0)ans.t+=(ll.rr-1)/2+1;}
else ans.rr=ll.rr+rr.rr;
return ans;
}
ans.ll=ll.ll;ans.rr=rr.rr;
if(rr.ll==0&&ll.rr==0){
if(rr.l==ll.r)ans.t--;
return ans;
}
if(ll.rr==0&&rr.ll!=0){
if(rr.l==ll.r)ans.t+=(rr.ll-1)/2;
else ans.t+=rr.ll/2;
return ans;
}
if(ll.rr!=0&&rr.ll==0){
if(rr.l==ll.r)ans.t+=(ll.rr-1)/2;
else ans.t+=ll.rr/2;
return ans;
}
if(rr.l==ll.r)ans.t+=std::min((rr.ll+ll.rr)/2,(rr.ll-1)/2+(ll.rr-1)/2+1);
else ans.t+=(rr.ll+ll.rr)/2;
return ans;
}
}
inline void optadd(lll o,lll l,lll r,lll ql,lll qr,lll v){
if(ql<=l&&qr>=r){vall[o]+=v;valr[o]+=v;addv[o]+=v;return;}
pushdown(o);
if(ql<=mid)optadd(lson,l,mid,ql,qr,v);
if(qr>mid)optadd(rson,mid+1,r,ql,qr,v);
pushup(o);
}
}seg;
int main(){
scanf("%lld",&n);
for(i=1;i<=n;i++)scanf("%lld",&tt[i]);
for(i=1;i<n;i++)a[i]=tt[i+1]-tt[i];
n--;//懶得處理了,直接--
seg.build(1,1,n);
scanf("%lld",&m);
while(m--){
scanf("%s",&ch);
if(ch[0]=='A'){
scanf("%lld%lld%lld%lld",&x,&y,&s,&t);
if(x>1)seg.optadd(1,1,n,x-1,x-1,s);
if(x<y)seg.optadd(1,1,n,x,y-1,t);
if(y<=n)seg.optadd(1,1,n,y,y,-s-(y-x)*t);
}else{
scanf("%lld%lld",&x,&y);
node fff;
if(x==y){printf("1\n");continue;}
fff=seg.querysum(1,1,n,x,y-1);
if(fff.t==0)printf("%lld\n",(y-x+2)/2);
else printf("%lld\n",std::min((y-x+2)/2,fff.t+(fff.ll+1)/2+(fff.rr+1)/2));
}
}
return 0;
}
這就屬於線段樹裡的難題了吧…以後這種不符合我能力的題,少碰…至於那道毒瘤的交通路線題…日後再說。
下一步填一下網路流的坑,問了問對面zhhx大佬,他初中就會網路流了…%%%差距很大啊,革命尚未成功…祝我一個月以後的省選能取得一個好成績吧,到時候去食堂弄十條炸肉捆起來擺在孔夫子面前,嗯…吾未嘗無誨焉。
網路流大概也是個大坑,數學建模能力…嗯就這樣。