1. 程式人生 > >suoi65 區間最小值的和 (單調棧+莫隊+rmq)

suoi65 區間最小值的和 (單調棧+莫隊+rmq)

傳送門

這題好妙妙啊

首先,如果我知道[l,r],要轉移到[l,r+1]的時候,就是加上以r+1為右端點,[l,r+1]為左端點的區間的最小值,其他情況和這個類似

考慮這玩意怎麼求

右端點固定的話,我左端點越往左走,這個最小值一定是越來越小

找到[l,r+1]範圍內的最小值mi,那麼在mi前面的第一個比mi小的位置(記為L[mi]),一定在這個範圍外

而且相同的左端點,以這個位置為右端點和以r+1為右端點,區間的最小值是相同的(都是這個數)

所以可以只記f[i]表示以i為右端點,左端點在[1,i]的最小值的和

再問[l,r+1]的話,那就是f[r+1]-f[L[mi]]再減掉左端點在[L[mi]+1,l-1]這個範圍內的,而這些的最小值都是mi

這個L用單調棧來求,mi用rmq來求

其他的轉移同理

 1 #include<bits/stdc++.h>
 2 #define CLR(a,x) memset(a,x,sizeof(a))
 3 using namespace std;
 4 typedef long long ll;
 5 typedef unsigned long long ull;
 6 typedef pair<int,int> pa;
 7 const int maxn=1e4+10,maxm=1e5+10;
 8 
 9 inline ll rd(){
10     ll x=0
;char c=getchar();int neg=1; 11 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 12 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 13 return x*neg; 14 } 15 16 int N,NN,M,a[maxn]; 17 int stk[maxn],sh; 18 int L[maxn],R[maxn]; 19 pa rmq[maxn][20]; 20 ll f[maxn],g[maxn];
21 struct Node{ 22 int l,r,i; 23 }que[maxm]; 24 ll ans[maxm]; 25 26 inline bool cmp(Node a,Node b){ 27 return a.l/NN==b.l/NN?a.r<b.r:a.l<b.l; 28 } 29 30 inline pa getmin(int l,int r){ 31 int m=log2(r-l+1); 32 return min(rmq[l][m],rmq[r-(1<<m)+1][m]); 33 } 34 35 inline void add(ll &n,int i,int bnd,int v){ 36 if(bnd<=i){ 37 pa p=getmin(bnd,i); 38 n+=v*(f[i]-f[L[p.second]]-1ll*p.first*(bnd-L[p.second]-1)); 39 }else{ 40 pa p=getmin(i,bnd); 41 n+=v*(g[i]-g[R[p.second]]-1ll*p.first*(R[p.second]-bnd-1)); 42 } 43 } 44 45 int main(){ 46 //freopen("","r",stdin); 47 int i,j,k; 48 N=rd(),M=rd();NN=sqrt(N); 49 for(i=1;i<=N;i++) a[i]=rd(); 50 for(i=N;i;i--){ 51 rmq[i][0]=make_pair(a[i],i); 52 for(j=1;(i+(1<<j)-1)<=N;j++) 53 rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]); 54 } 55 for(i=1;i<=N;i++){ 56 while(sh&&a[stk[sh]]>a[i]){ 57 R[stk[sh--]]=i; 58 }stk[++sh]=i; 59 }sh=0; 60 for(i=N;i;i--){ 61 while(sh&&a[stk[sh]]>a[i]){ 62 L[stk[sh--]]=i; 63 }stk[++sh]=i; 64 } 65 for(i=1;i<=N;i++){ 66 f[i]=f[L[i]]+1ll*(i-L[i])*a[i]; 67 } 68 for(i=N;i;i--){ 69 if(!R[i]) R[i]=N+1; 70 g[i]=g[R[i]]+1ll*(R[i]-i)*a[i]; 71 } 72 73 for(i=1;i<=M;i++){ 74 que[i].l=rd(),que[i].r=rd(),que[i].i=i; 75 } 76 sort(que+1,que+M+1,cmp); 77 int l=1,r=0;ll n=0; 78 for(i=1;i<=M;i++){ 79 while(r<que[i].r) add(n,++r,l,1); 80 while(r>que[i].r) add(n,r--,l,-1); 81 while(l<que[i].l) add(n,l++,r,-1); 82 while(l>que[i].l) add(n,--l,r,1); 83 ans[que[i].i]=n; 84 } 85 for(i=1;i<=M;i++) 86 printf("%lld\n",ans[i]); 87 return 0; 88 } 89 /* 90 9 2 91 1 7 5 2 6 4 3 5 8 92 1 6 93 3 8 94 */