【BZOJ4540】 [HNOI2016] 序列(莫隊)
阿新 • • 發佈:2018-12-02
大致題意: 求出一個序列的一段區間中所有子序列最小值之和。
莫隊
這道題其實是一道莫隊題。
但是需要大量的預處理。
預處理
先考慮預處理兩個陣列\(lst_i\)和\(nxt_i\),分別表示在第\(i\)個元素左邊、右邊第一個小於它的元素的位置。
這可以直接用單調棧實現\(O(n)\)預處理。
然後,我們考慮預處理出\(sum_i\)和\(lst_i\)兩個陣列,分別儲存在\([1,i]\)區間右側插入一個數的代價和在\([i,n]\)區間左側插入一個數的代價,即\(\sum_{k=1}^i Min_{x=1}^k a_x\)和\(\sum_{k=i}^n Min_{x=k}^n a_x\)
這可以在預處理\(lst_i\)和\(nxt_i\)的同時這樣預處理:
sum[i]=sum[lst[i]]+1LL*a[i]*(i-lst[i]);//suf陣列同理
區間移動
接下來,我們要考慮如何處理區間移動(以在左邊插入一個數為例)。
首先,我們找到這個區間內最小的數所在的位置\(p\),顯然,第\([p,R]\)的位置與\(L\)所構成的區間的最小值都是\(a_p\)。
然後就考慮\([L,p-1]\)區間內的貢獻。
考慮到\([L,p]\)區間內\(a_p\)最小,因此\(suf_L\)值肯定是從\(suf_p\)經過若干次轉移得來的。
因此它就滿足字首和性質了,可以直接通過\(suf_L-suf_p\)
而其他的區間移動操作也是同理的,具體實現詳見程式碼。
程式碼
#include<bits/stdc++.h> #define N 100000 #define LL long long #define INF 1e9 #define Gmin(x,y) (x>(y)&&(x=(y))) using namespace std; int n,query_tot,a[N+5]; class Class_FIO { private: #define Fsize 100000 #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++) #define pc(ch) (void)(FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch)) int f,Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize]; public: Class_FIO() {A=B=Fin;} inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;} inline void writeln(LL x) {if(!x) return pc('0'),pc('\n');x<0&&(pc('-'),x=-x);while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);pc('\n');} inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;} }F; class Class_CaptainMotao { private: #define AddLeft() {register int p=RMQ.GetMin(--L,R);res+=1LL*a[p]*(R-p+1)+I.suf[L]-I.suf[p];} #define AddRight() {register int p=RMQ.GetMin(L,++R);res+=1LL*a[p]*(p-L+1)+I.sum[R]-I.sum[p];} #define DelLeft() {register int p=RMQ.GetMin(L,R);res-=1LL*a[p]*(R-p+1)+I.suf[L++]-I.suf[p];} #define DelRight() {register int p=RMQ.GetMin(L,R);res-=1LL*a[p]*(p-L+1)+I.sum[R--]-I.sum[p];} int S;LL ans[N+5]; class Class_Initer//初始化 { private: int Top,lst[N+5],nxt[N+5],Stack[N+5]; public: LL sum[N+5],suf[N+5]; inline void Init() { register int i; for(i=1;i<=n;++i) {while(Top&&a[Stack[Top]]>=a[i]) --Top;lst[i]=Stack[Top],Stack[++Top]=i,sum[i]=sum[lst[i]]+1LL*a[i]*(i-lst[i]);} for(Stack[Top=0]=n+1,i=n;i;--i) {while(Top&&a[Stack[Top]]>=a[i]) --Top;nxt[i]=Stack[Top],Stack[++Top]=i,suf[i]=suf[nxt[i]]+1LL*a[i]*(nxt[i]-i);} } }I; class Class_RMQ//RMQ { private: #define LogN 20 int Log2[N+5]; struct RMQ_data { int val,pos; inline friend bool operator < (RMQ_data x,RMQ_data y) {return x.val<y.val;} RMQ_data(int x=0,int y=0):val(x),pos(y){} }Min[N+5][LogN+5]; public: inline void Init() { register int i,j; for(i=2;i<=n;++i) Log2[i]=Log2[i>>1]+1; for(i=1;i<=n;++i) Min[i][0]=RMQ_data(a[i],i); for(j=1;(1<<j-1)<=n;++j) for(i=1;i+(1<<j-1)<=n;++i) Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]); } inline int GetMin(int l,int r) {register int k=Log2[r-l+1];return min(Min[l][k],Min[r-(1<<k)+1][k]).pos;} }RMQ; struct Query { int l,r,pos,bl; inline friend bool operator < (Query x,Query y) {return x.bl^y.bl?x.bl<y.bl:(x.bl&1?x.r<y.r:x.r>y.r);} }q[N+5]; public: inline void Solve() { register int i,L=1,R=0;register LL res=0; for(I.Init(),RMQ.Init(),S=sqrt(n),i=1;i<=query_tot;++i) F.read(q[q[i].pos=i].l),F.read(q[i].r),q[i].bl=(q[i].l-1)/S+1; for(sort(q+1,q+query_tot+1),i=1;i<=query_tot;++i)//處理詢問 { while(R<q[i].r) AddRight(); while(L>q[i].l) AddLeft(); while(R>q[i].r) DelRight(); while(L<q[i].l) DelLeft(); ans[q[i].pos]=res; } for(i=1;i<=query_tot;++i) F.writeln(ans[i]); } }C; int main() { register int i; for(F.read(n),F.read(query_tot),i=1;i<=n;++i) F.read(a[i]); return C.Solve(),F.clear(),0; }