P3246 [HNOI2016]序列 莫队 + ST表 + 单调栈
傳送門
文章目錄
- 題意:
- 思路:
- Update
題意:
思路:
比較神奇的一個題,這里先介紹莫隊的離線解法。
不難發現,用莫隊來做最大的難點就是在進行區間移動的時候如何快速計算貢獻。
比如[l,r]?>[l,r+1][l,r]->[l,r+1][l,r]?>[l,r+1]的時候,多出來的區間就是[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[i]f[i]f[i]表示以iii為結尾的每個后綴的答案,要保證這些后綴都是不交的區間,即后綴的若干區間應該是類似于這種形式[x,i],[y,x?1],...,[1,h?1][x,i],[y,x-1],...,[1,h-1][x,i],[y,x?1],...,[1,h?1],為了轉移方便,考慮記pre[i]pre[i]pre[i]表示左邊第一個≤a[i]\le a[i]≤a[i]的位置,這個用單調棧可以很快維護出來,轉移方程:f[i]=(i?pre[i])?a[i]+f[pre[i]]f[i]=(i-pre[i])*a[i]+f[pre[i]]f[i]=(i?pre[i])?a[i]+f[pre[i]]
考慮這個怎么用,顯然我們不能直接加上f[r+1]f[r+1]f[r+1],因為f[r+1]f[r+1]f[r+1]包含了[1,x],[x+1,y],...,[z+1,r+1][1,x],[x+1,y],...,[z+1,r+1][1,x],[x+1,y],...,[z+1,r+1]這若干段區間的貢獻,而我們要算的貢獻是[l,r+1][l,r+1][l,r+1]之間的貢獻,顯然這兩個區間是有可能不交的,當然也不能寫成f[r+1]?f[l?1]f[r+1]-f[l-1]f[r+1]?f[l?1]的形式,除非f[r+1]f[r+1]f[r+1]包含的區間中幾段拼起來正好有[l,r+1][l,r+1][l,r+1],這樣才可以。那么我們往這個思路上靠,是否能找到個位置pospospos,使得[pos+1,r+1][pos+1,r+1][pos+1,r+1]的區間能通過f[r+1]?f[pos]f[r+1]-f[pos]f[r+1]?f[pos]直接得到貢獻,并且[l,pos][l,pos][l,pos]區間的貢獻也能快速計算呢?考慮求fff數組的過程,我們是找左邊≤a[i]\le a[i]≤a[i]的最近的位置,那么我們要是能找到[l,r+1][l,r+1][l,r+1]中的最小值的位置,就可以計算了!顯然這個可以STSTST表預處理出來,之后再預處理一下另一邊的即可,那么這個題就結束啦,代碼還是比較好調的。
實在有點不明白為什么這個題莫隊的指針移動順序對答案有影響,按照莫隊的原理來說順序是隨意的,可能這個先加后減是有什么影響,真是為數不多指針移動順序的有影響的題了。。。等弄懂了再寫原因吧。
復雜度O(nlogn+nn)O(nlogn+n\sqrt n)O(nlogn+nn?)
Update
在線做法:
繼續考慮我們的fff數組,我們還是找一個最小的位置pospospos,讓后分成兩個區間[l,pos),(pos,r][l,pos),(pos,r][l,pos),(pos,r],當左端點在左邊區間,右端點在右邊區間的時候,貢獻就是(pos?l+1)?(r?pos+1)?a[pos](pos-l+1)*(r-pos+1)*a[pos](pos?l+1)?(r?pos+1)?a[pos],現在就剩左右端點分別在左邊和右邊區間了,這里只考慮做右端點都在右邊的,在左邊同理。
考慮如果右端點在rrr,左端點在(pos,r](pos,r](pos,r]的時候的答案為f[r]?f[pos]f[r]-f[pos]f[r]?f[pos],右端點在r?1r-1r?1,左端點在(pos,r?1](pos,r-1](pos,r?1]的時候答案為f[r?1]?f[pos]f[r-1]-f[pos]f[r?1]?f[pos]…,我們定義sumi=∑j=1ifjsum_i=\sum_{j=1}^if_jsumi?=∑j=1i?fj?,那么在(pos,r](pos,r](pos,r]的貢獻就是gr?gpos?(r?pos)?fposg_r-g_{pos}-(r-pos)*f_{pos}gr??gpos??(r?pos)?fpos?,右邊區間同理。
復雜度O(nlogn)O(nlogn)O(nlogn)
O(nlogn+nn)O(nlogn+n\sqrt n)O(nlogn+nn?)
// 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("Ofast,no-stack-protector,unroll-loops,fast-math") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native") //#pragma GCC optimize(2) #include<cstdio> #include<iostream> #include<string> #include<cstring> #include<map> #include<cmath> #include<cctype> #include<vector> #include<set> #include<queue> #include<algorithm> #include<sstream> #include<ctime> #include<cstdlib> #include<random> #include<cassert> #define X first #define Y second #define L (u<<1) #define R (u<<1|1) #define pb push_back #define mk make_pair #define Mid ((tr[u].l+tr[u].r)>>1) #define Len(u) (tr[u].r-tr[u].l+1) #define random(a,b) ((a)+rand()%((b)-(a)+1)) #define db puts("---") using namespace std;//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); } //void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); } //void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> PII;const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f; const double eps=1e-6;int n,m; int a[N],block; int stk[N],top,suf[N],pre[N]; LL psum[N],ssum[N]; struct Node {int l,r,id; }q[N];bool cmp(Node a,Node b) {int la=(a.l-1)/block+1,lb=(b.l-1)/block+1;return la^lb? la<lb : ((la&1)? a.r>b.r:a.r<b.r); }int f[N][30]; double len[N]; void init() {for(int i=1;i<=n;i++) f[i][0]=i;int t=log2(n)+1;for(int j=1;j<t;j++)for(int i=1;i<=n-(1<<j)+1;i++) {if(a[f[i][j-1]]<a[f[i+(1ll<<(j-1))][j-1]]) f[i][j]=f[i][j-1];else if(a[f[i][j-1]]>a[f[i+(1ll<<(j-1))][j-1]]) f[i][j]=f[i+(1ll<<(j-1))][j-1];else if(a[f[i][j-1]]==a[f[i+(1ll<<(j-1))][j-1]]) f[i][j]=max(f[i][j-1],f[i+(1ll<<(j-1))][j-1]);} }int query(int l,int r) {int t=len[r-l+1];if(a[f[l][t]]<a[f[r-(1<<t)+1][t]]) return f[l][t];else if(a[f[l][t]]>a[f[r-(1<<t)+1][t]]) return f[r-(1<<t)+1][t];return max(f[l][t],f[r-(1<<t)+1][t]); }LL ans[N],sum;void addl(int l,int r) {int pos=query(l,r);sum+=1ll*(r-pos+1)*a[pos]+ssum[l]-ssum[pos]; }void addr(int l,int r) {int pos=query(l,r);sum+=1ll*(pos-l+1)*a[pos]+psum[r]-psum[pos]; }void dell(int l,int r) {int pos=query(l,r);sum-=1ll*(r-pos+1)*a[pos]+ssum[l]-ssum[pos]; }void delr(int l,int r) {int pos=query(l,r);sum-=1ll*(pos-l+1)*a[pos]+psum[r]-psum[pos]; }int main() { // ios::sync_with_stdio(false); // cin.tie(0);cin>>n>>m; block=sqrt(n);for(int i=1;i<=n;i++) len[i]=log2(i);for(int i=1;i<=n;i++) scanf("%d",&a[i]); init();for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;sort(q+1,q+1+m,cmp);for(int i=1;i<=n;i++) {while(top&&a[stk[top]]>a[i]) suf[stk[top--]]=i;pre[i]=stk[top]; stk[++top]=i;}while(top) pre[stk[top]]=stk[top-1],suf[stk[top--]]=n+1;for(int i=1;i<=n;i++) psum[i]=1ll*(i-pre[i])*a[i]+psum[pre[i]];for(int i=n;i>=1;i--) ssum[i]=1ll*(suf[i]-i)*a[i]+ssum[suf[i]];int l=1,r=0;for(int i=1;i<=m;i++) {int lx=q[i].l,rx=q[i].r;while(l>lx) addl(--l,r);while(r<rx) addr(l,++r);while(r>rx) delr(l,r--);while(l<lx) dell(l++,r);ans[q[i].id]=sum;}for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);return 0; }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("Ofast,no-stack-protector,unroll-loops,fast-math") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native") //#pragma GCC optimize(2) #include<cstdio> #include<iostream> #include<string> #include<cstring> #include<map> #include<cmath> #include<cctype> #include<vector> #include<set> #include<queue> #include<algorithm> #include<sstream> #include<ctime> #include<cstdlib> #include<random> #include<cassert> #define X first #define Y second #define L (u<<1) #define R (u<<1|1) #define pb push_back #define mk make_pair #define Mid ((tr[u].l+tr[u].r)>>1) #define Len(u) (tr[u].r-tr[u].l+1) #define random(a,b) ((a)+rand()%((b)-(a)+1)) #define db puts("---") using namespace std;//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); } //void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); } //void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> PII;const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f; const double eps=1e-6;int n,m; int a[N],block; int stk[N],top; LL suf[N],pre[N]; LL psum[N],ssum[N];int f[N][30]; double len[N]; void init() {for(int i=1;i<=n;i++) f[i][0]=i;int t=log2(n)+1;for(int j=1;j<t;j++)for(int i=1;i<=n-(1<<j)+1;i++) {if(a[f[i][j-1]]<a[f[i+(1ll<<(j-1))][j-1]]) f[i][j]=f[i][j-1];else if(a[f[i][j-1]]>a[f[i+(1ll<<(j-1))][j-1]]) f[i][j]=f[i+(1ll<<(j-1))][j-1];else if(a[f[i][j-1]]==a[f[i+(1ll<<(j-1))][j-1]]) f[i][j]=max(f[i][j-1],f[i+(1ll<<(j-1))][j-1]);} }int query(int l,int r) {int t=len[r-l+1];if(a[f[l][t]]<a[f[r-(1<<t)+1][t]]) return f[l][t];else if(a[f[l][t]]>a[f[r-(1<<t)+1][t]]) return f[r-(1<<t)+1][t];return max(f[l][t],f[r-(1<<t)+1][t]); }int main() { // ios::sync_with_stdio(false); // cin.tie(0);cin>>n>>m; for(int i=1;i<=n;i++) len[i]=log2(i);for(int i=1;i<=n;i++) scanf("%d",&a[i]); init();for(int i=1;i<=n;i++) {while(top&&a[stk[top]]>a[i]) suf[stk[top--]]=i;pre[i]=stk[top]; stk[++top]=i;}while(top) pre[stk[top]]=stk[top-1],suf[stk[top--]]=n+1;for(int i=1;i<=n;i++) psum[i]=1ll*(i-pre[i])*a[i]+psum[pre[i]];for(int i=n;i>=1;i--) ssum[i]=1ll*(suf[i]-i)*a[i]+ssum[suf[i]];for(int i=1;i<=n;i++) pre[i]=pre[i-1]+psum[i];for(int i=n;i>=1;i--) suf[i]=suf[i+1]+ssum[i];while(m--) {int l,r; scanf("%d%d",&l,&r);int pos=query(l,r);LL ans=1ll*(pos-l+1)*(r-pos+1)*a[pos];ans+=pre[r]-pre[pos]-psum[pos]*(r-pos);ans+=suf[l]-suf[pos]-ssum[pos]*(pos-l);printf("%lld\n",ans);}return 0; }總結
以上是生活随笔為你收集整理的P3246 [HNOI2016]序列 莫队 + ST表 + 单调栈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: #3771. Triple 生成函数 +
- 下一篇: 陈皮红茶的功效与作用、禁忌和食用方法