1. 程式人生 > >【題解】HNOI-2016序列

【題解】HNOI-2016序列

Problem

Solution

這道題在HNOI2016中還算是好的了……

這題中如若去掉多組詢問的話可以在O(nlogn)的時間內得解(並查集),但多組詢問必定要優化,發現這種其他結構基本上無法涉足的題目就只能上莫隊了(我也不知道為啥想到莫隊,可能這就是題感吧)

減去O(nn)只剩下常數時間可供轉移了,那麼就要想法常數轉移

明顯在一個序列變化時答案的變化值取決於邊界消長節點與序列中每一個節點所組成區間的答案,那麼一定只有預處理才能常數解決

考慮一個數ai只有在前面第一個比其小的元素後面,後面第一個比其小的元素前面的區間才有效,那麼預處理出

pre[]nxt[]陣列分別表示每個元素前後第一個比其小的元素的位置,明顯可以用單調棧O(n)解決(不會的看程式碼),再根據這兩個陣列預處理出一個貢獻字首和與一個貢獻字尾和,那麼轉移的大體貢獻就可以根據這兩個前/字尾和做差得到

剩下的就只有區間的邊緣部分,邊緣部分只用快速查詢邊緣部分中的最小值即可,如果也要用常數時間查詢,蒟蒻就只知道st表了

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rg register
#define cl(x) memset(x,0,sizeof(x))
#define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)<(y)?(x):(y)) #define abs(x) ((x)>0?(x):(-(x))) template <typename _Tp> inline _Tp read(_Tp&x){ rg char c11=getchar(),ob=0;x=0; while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1; while
(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x; } const int N=100100; struct Query{int l,r,id;}qu[N]; ll a[N],suml[N],sumr[N],Ans[N]; int st[N][18],Log[N],pre[N],nxt[N],sta[N],top(0); int n,m; inline int cmp1(const Query&aa,const Query&bb){return aa.l<bb.l;} inline int cmp2(const Query&aa,const Query&bb){return aa.r<bb.r;} inline int query(int l,int r){ #define get_(x,y) (a[x]<a[y]?(x):(y)) return get_(st[l][Log[r-l+1]],st[r-(1<<Log[r-l+1])+1][Log[r-l+1]]); #undef get_ } void init(); void pre_pre_suf(); void pre_st(); void pre_pointer(); void div_block(); void Captain_Mo(){ rg int l(1),r(1),pos;rg ll ans(a[1]); for(rg int i=1;i<=m;++i){ while(r<qu[i].r){++r;pos=query(l,r);ans+=a[pos]*(pos-l+1)+suml[r]-suml[pos];} while(qu[i].l<l){--l;pos=query(l,r);ans+=a[pos]*(r-pos+1)+sumr[l]-sumr[pos];} while(qu[i].r<r){pos=query(l,r);ans-=a[pos]*(pos-l+1)+suml[r]-suml[pos];--r;} while(l<qu[i].l){pos=query(l,r);ans-=a[pos]*(r-pos+1)+sumr[l]-sumr[pos];++l;} Ans[qu[i].id]=ans; } return ; } void Print(); int main(){ init(); pre_pointer(); pre_st(); pre_pre_suf(); div_block(); Captain_Mo(); Print(); return 0; } void pre_pre_suf(){ for(rg int i=1;i<=n;++i)suml[i]=suml[pre[i]]+(i-pre[i])*a[i]; for(rg int i=n;i;--i) sumr[i]=sumr[nxt[i]]+(nxt[i]-i)*a[i]; return ; } void Print(){for(rg int i=1;i<=m;++i)printf("%lld\n",Ans[i]);return ;} void div_block(){ sort(qu+1,qu+m+1,cmp1); int block=sqrt(1.0*m); for(rg int i=1;i<=m;i+=block) sort(qu+i,qu+min(i+block,m),cmp2); return ; } void pre_st(){ #define get_(x,y) (a[(x)]<a[(y)]?(x):(y)) Log[0]=-1; for(rg int i=1;i<=n;++i) Log[i]=Log[i>>1]+1,st[i][0]=i; for(rg int i=n;i;--i) for(rg int j=1;i+(1<<j)-1<=n;++j) st[i][j]=get_(st[i][j-1],st[i+(1<<(j-1))][j-1]); return ; #undef get_ } void pre_pointer(){ for(rg int i=1;i<=n;++i){ while(top&&a[sta[top]]>a[i])nxt[sta[top]]=i,--top; pre[i]=sta[top]; sta[++top]=i; } return ; } void init(){ read(n),read(m); for(rg int i=1;i<=n;++i)read(a[i]); for(rg int i=1;i<=m;++i)read(qu[i].l),read(qu[i].r),qu[i].id=i; return ; }