日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

洛谷 - P3246 [HNOI2016]序列(莫队+单调栈)

發布時間:2024/4/11 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 洛谷 - P3246 [HNOI2016]序列(莫队+单调栈) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:給出一個長度為 nnn 的序列,再給出 mmm 次詢問,每次詢問需要回答一個區間 [l,r][l,r][l,r] 內所有子區間的最小值之和

題目分析:因為可以離線,所以考慮莫隊,這個題的難點是如何處理 [L,R][L,R][L,R] 遞推到 [L,R+1][L,R+1][L,R+1]

[L,R][L,R][L,R] 推到 [L,R+1][L,R+1][L,R+1],實質上多了 R?L+2R-L+2R?L+2 個子區間,分別是 [L,R+1],[L+1,R+1],...,[R+1,R+1][L,R+1],[L+1,R+1],...,[R+1,R+1][L,R+1],[L+1,R+1],...,[R+1,R+1],我們只需要將這些子區間的最小值之和加上就好了

所以不難想到需要維護一個輔助數組 f[r]f[r]f[r],代表前綴 [1:r][1:r][1:r] 的所有后綴 [1:r],[2:r],...,[r:r][1:r],[2:r],...,[r:r][1:r],[2:r],...,[r:r] 的最小值之和,轉移的話可以利用單調棧維護出 l[r]l[r]l[r] 代表 rrr 左側首個小于 a[r]a[r]a[r] 的位置,那么有 f[r]=f[l[r]]+(r?l[r])?a[r]f[r]=f[l[r]]+(r-l[r])*a[r]f[r]=f[l[r]]+(r?l[r])?a[r],具體意義就是 a[r]a[r]a[r] 充當了左端點位于 (l[r],r](l[r],r](l[r],r] 的這些后綴的最小值

現在我們需要借助 fff 數組快速求出以 R+1R+1R+1 為右端點,左端點位于區間 [L,R+1][L,R+1][L,R+1] ,右端點為 R+1R+1R+1 的子區間的最小值之和,下文稱最小值之和貢獻

設區間 [L,R+1][L,R+1][L,R+1] 的最小值的位置為 ppp,不難看出左端點位于 [L,p][L,p][L,p] 的子區間的貢獻為 a[p]a[p]a[p],現在我們的問題變成如何計算左端點位于 (p,R+1](p,R+1](p,R+1] 的子區間的貢獻

用我們剛才的 fff 公式展開看看?

x=R+1x=R+1x=R+1f[x]=f[l[x]]+(x?l[x])?a[x]f[x]=f[l[x]]+(x-l[x])*a[x]f[x]=f[l[x]]+(x?l[x])?a[x]

pre=l[x]pre=l[x]pre=l[x]f[pre]=f[l[pre]]+(pre?l[pre])?a[pre]f[pre]=f[l[pre]]+(pre-l[pre])*a[pre]f[pre]=f[l[pre]]+(pre?l[pre])?a[pre]

ppre=l[pre]ppre=l[pre]ppre=l[pre]f[ppre]=f[l[ppre]]+(ppre?l[ppre])?a[ppre]f[ppre]=f[l[ppre]]+(ppre-l[ppre])*a[ppre]f[ppre]=f[l[ppre]]+(ppre?l[ppre])?a[ppre]

不難發現我們會沿著一個 a[pre]a[pre]a[pre] 遞減的序列一直迭代,即 a[ppre]<a[pre]<a[x]a[ppre]<a[pre]<a[x]a[ppre]<a[pre]<a[x],并且直到 ppp 停止,因為 a[p]a[p]a[p] 是區間 [L,R+1][L,R+1][L,R+1] 中的最小值呀,a[p]a[p]a[p] 前面不會再有比它小的數字了

如果將上面的公式自底向上套進去的話,我們會驚奇的發現,f[R+1]?f[p]f[R+1]-f[p]f[R+1]?f[p] 即為所求,也就是左端點位于 (p,R+1](p,R+1](p,R+1] ,右端點為 R+1R+1R+1 的這些子區間的貢獻

到此為止我們可以利用 RMQRMQRMQ 預處理區間最小值的位置然后 O(1)O(1)O(1) 進行區間 [L,R][L,R][L,R][L,R+1][L,R+1][L,R+1] 的擴展了,對于剩下的 [L,R?1],[L+1,R],[L?1,R][L,R-1],[L+1,R],[L-1,R][L,R?1],[L+1,R],[L?1,R] ,分析起來同理,這里不做過多贅述

有一個小問題就是,這個題的莫隊的修改順序需要先加后減,不然會又WA又RE的

那么莫隊的時間復雜度就是 O(nlogn+nn)=O(nn)O(nlogn+n\sqrt{n})=O(n\sqrt{n})O(nlogn+nn?)=O(nn?)


打一個分割線,下面講講另一種寫法

注意到這個題很巧妙地預處理出了 fff 數組輔助莫隊的轉移,所以我們既然想到了 fff 數組,為什么不思考一下能否直接用 fff 數組計算區間的貢獻呢?

給定區間 [L,R][L,R][L,R],還是設 ppp 為區間最小值的位置,我們將總貢獻分成三份:區間 [L,R][L,R][L,R] 的貢獻 = 區間 [L,p)[L,p)[L,p) 的貢獻 + 區間 (p,R](p,R](p,R] 的貢獻 + (p?L+1)?(R?p+1)?a[p](p-L+1)*(R-p+1)*a[p](p?L+1)?(R?p+1)?a[p]

上述公式第三項 (p?L+1)?(R?p+1)?a[p](p-L+1)*(R-p+1)*a[p](p?L+1)?(R?p+1)?a[p] ,意思就是左端點位于 [L,p][L,p][L,p],同時右端點位于 [p,R][p,R][p,R] 的子區間的貢獻

這里我們以區間 (p,R](p,R](p,R] 的貢獻為例,分析一下該如何計算

前面的莫隊解法我們提到了,f[R]?f[p]f[R]-f[p]f[R]?f[p] 代表的是,右端點為 RRR,左端點位于 (p,R](p,R](p,R] 的子區間的貢獻

那么擴展到 f[R?1]?f[p]f[R-1]-f[p]f[R?1]?f[p] 是不是也可以代表,右端點為 R?1R-1R?1,左端點位于 (p,R?1](p,R-1](p,R?1] 的子區間的貢獻?

所以區間 (p,R](p,R](p,R] 的貢獻就可以寫成(f[R]?f[p])+(f[R?1]?f[p])+...+(f[p+1]?f[p])(f[R]-f[p])+(f[R-1]-f[p])+...+(f[p+1]-f[p])(f[R]?f[p])+(f[R?1]?f[p])+...+(f[p+1]?f[p])

g[i]=∑j=1jf[j]g[i]=\sum\limits_{j=1}^{j}f[j]g[i]=j=1j?f[j],也就是數組 fff 的一個前綴和,那么上述公式就等價于 g[R]?g[p]?f[p]?(R?p)g[R]-g[p]-f[p]*(R-p)g[R]?g[p]?f[p]?(R?p)

所以現在本題的時空復雜度就變成 RMQRMQRMQ 預處理的復雜度了,也就是 O(nlogn)O(nlogn)O(nlogn),且強制在線

代碼:
離線

// Problem: P3246 [HNOI2016]序列 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P3246 // Memory Limit: 500 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org)// #pragma GCC optimize(2) // #pragma GCC optimize("Ofast","inline","-ffast-math") // #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> #define lowbit(x) x&-x using namespace std; typedef long long LL; typedef unsigned long long ull; template<typename T> inline void read(T &x) {T f=1;x=0;char ch=getchar();while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();x*=f; } template<typename T> inline void write(T x) {if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0'); } const int inf=0x3f3f3f3f; const int N=1e5+100; int size,n,m,a[N],l[N],r[N],mmin[N][20],Log[N]; LL fl[N],fr[N],ans[N]; struct query {int l,r,id;bool operator<(const query& a)const{if(l/size!=a.l/size)return l<a.l;else if((l/size)&1)return r<a.r;elsereturn r>a.r;} }q[N]; int cmp(int x,int y) {return a[x]<a[y]?x:y; } int ask(int l,int r) {int k=Log[r-l+1];return cmp(mmin[l][k],mmin[r-(1<<k)+1][k]); } LL left(int l,int r) {int p=ask(l,r);return 1LL*(r-p+1)*a[p]+fl[l]-fl[p]; } LL right(int l,int r) {int p=ask(l,r);return 1LL*(p-l+1)*a[p]+fr[r]-fr[p]; } void solve() {int l=1,r=0;LL sum=0;for(int i=1;i<=m;i++){int ql=q[i].l;int qr=q[i].r;while(l>ql) sum+=left(l-1,r),l--;while(r<qr) sum+=right(l,r+1),r++;while(l<ql) sum-=left(l,r),l++;while(r>qr) sum-=right(l,r),r--;ans[q[i].id]=sum;} } void init() {size=313;//單調棧stack<int>st;for(int i=1;i<=n;i++) {while(st.size()&&a[st.top()]>a[i]) st.pop();if(st.empty()) l[i]=0;else l[i]=st.top();st.push(i);}while(st.size()) st.pop();for(int i=n;i>=1;i--) {while(st.size()&&a[st.top()]>a[i]) st.pop();if(st.empty()) r[i]=n+1;else r[i]=st.top();st.push(i);}//RMQfor(int i=1;i<=n;i++)mmin[i][0]=i,Log[i]=log2(i);for(int i=1;i<20;i++)for(int j=1;j+(1<<i)-1<=n;j++)mmin[j][i]=cmp(mmin[j][i-1],mmin[j+(1<<(i-1))][i-1]);//dpfor(int i=1;i<=n;i++) fr[i]=fr[l[i]]+1LL*(i-l[i])*a[i];for(int i=n;i>=1;i--) fl[i]=fl[r[i]]+1LL*(r[i]-i)*a[i]; } int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);read(n),read(m);for(int i=1;i<=n;i++) {read(a[i]);}init();for(int i=1;i<=m;i++) {read(q[i].l),read(q[i].r),q[i].id=i;}sort(q+1,q+1+m);solve();for(int i=1;i<=m;i++) {printf("%lld\n",ans[i]);}return 0; }

在線

// Problem: P3246 [HNOI2016]序列 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P3246 // Memory Limit: 500 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org)// #pragma GCC optimize(2) // #pragma GCC optimize("Ofast","inline","-ffast-math") // #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> #define lowbit(x) x&-x using namespace std; typedef long long LL; typedef unsigned long long ull; template<typename T> inline void read(T &x) {T f=1;x=0;char ch=getchar();while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();x*=f; } template<typename T> inline void write(T x) {if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0'); } const int inf=0x3f3f3f3f; const int N=1e5+100; int n,m,a[N],l[N],r[N],mmin[N][20],Log[N]; LL fl[N],fr[N],gl[N],gr[N]; int cmp(int x,int y) {return a[x]<a[y]?x:y; } int ask(int l,int r) {int k=Log[r-l+1];return cmp(mmin[l][k],mmin[r-(1<<k)+1][k]); } void init() {//單調棧stack<int>st;for(int i=1;i<=n;i++) {while(st.size()&&a[st.top()]>a[i]) st.pop();if(st.empty()) l[i]=0;else l[i]=st.top();st.push(i);}while(st.size()) st.pop();for(int i=n;i>=1;i--) {while(st.size()&&a[st.top()]>a[i]) st.pop();if(st.empty()) r[i]=n+1;else r[i]=st.top();st.push(i);}//RMQfor(int i=1;i<=n;i++)mmin[i][0]=i,Log[i]=log2(i);for(int i=1;i<20;i++)for(int j=1;j+(1<<i)-1<=n;j++)mmin[j][i]=cmp(mmin[j][i-1],mmin[j+(1<<(i-1))][i-1]);//dpfor(int i=1;i<=n;i++) fr[i]=fr[l[i]]+1LL*(i-l[i])*a[i],gr[i]=gr[i-1]+fr[i];for(int i=n;i>=1;i--) fl[i]=fl[r[i]]+1LL*(r[i]-i)*a[i],gl[i]=gl[i+1]+fl[i]; } int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);read(n),read(m);for(int i=1;i<=n;i++) {read(a[i]);}init();while(m--) {int l,r;read(l),read(r);int p=ask(l,r);printf("%lld\n",1LL*(p-l+1)*(r-p+1)*a[p]+gr[r]-gr[p]-fr[p]*(r-p)+gl[l]-gl[p]-fl[p]*(p-l));}return 0; }

總結

以上是生活随笔為你收集整理的洛谷 - P3246 [HNOI2016]序列(莫队+单调栈)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。