一個隊列內部的元素具備單調性的一種數據結構,分爲單調遞增隊列和單調遞減隊列。
單調遞減隊列隊首元素爲區間最大值,隊尾元素爲最近的大於新元素的元素下標。
單調遞減序列維護區間最小值和最近的小於新元素的元素下標。ios
int l=1,r=0; for(int i=1;i<=n;i++){ while(r>=l&&a[q[r]]>a[i]) r--; q[++r]=i; }
給定一個n個數的數列,從左至右輸出每一個長度爲m的區間內的最小數。數據結構
int l=1,r=0; for(int i=1;i<=n;i++){ while(r>=l&&a[q[r]]>=a[i]) r--; q[++r]=i; while(r>=l && q[l]<i-m+1)l++; }
給定一個長度爲n的數列,每個點分別有 x,y 兩個值,找到最長的區間,知足區間中 max(x[i]) - min(y[i]) <= kspa
int l1=1,r1=0,l2=1,r2=0,now=1; for(int i=1;i<=n;i++){ while(r1>=l1&&a[q1[r1]]<a[i]) r1--;//單調不嚴格減 while(r2>=l2&&a[q2[r2]]>a[i]) r2--;//單調不嚴格增 q1[++r1]=i; q2[++r2]=i; while(r1>=l1&&r2>=l2&&a[q1[l1]]-a[q2[l2]]>k){ if(q1[l1]<q2[l2]) now=q1[l1++]+1;//先移動座標小的 else now=q2[l2++]+1; } ans=max(ans,i-now+1); }
給定區間\((1,n)\),定義\(f(a,b)\)爲區間和*區間最小值,求\(min(f(a,b)),1\le a \le n,1 \le b\le n\).net
解法:
將問題轉化爲,求每一個元素做爲最小值時的最大區間,計算出每一個區間的貢獻,對答案求maxcode
//用stl_stack的寫法,常數大不推薦 #include <iostream> #include <cstdio> #include <stack> using namespace std; typedef long long ll; const int maxn=1e5+5; ll a[maxn],sum[maxn],p1[maxn],p2[maxn]; int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); sum[i]=a[i]+sum[i-1]; } stack<ll>s; for(int i=1;i<=n;i++){ while(!s.empty()&&a[s.top()]>=a[i]) s.pop(); if(s.empty()) p1[i]=1; else p1[i]=s.top()+1; s.push(i); } while(!s.empty()) s.pop(); for(int i=n;i>=1;i--){ while(!s.empty()&&a[s.top()]>=a[i]) s.pop(); if(s.empty()) p2[i]=n; else p2[i]=s.top()-1; s.push(i); } ll ans=-1,tx=0,ty=0; for(int i=1;i<=n;i++){ ll temp=(sum[p2[i]]-sum[p1[i]-1])*a[i]; if(temp>ans){ ans=temp; tx=p1[i];ty=p2[i]; } } printf("%lld\n%lld %lld",ans,tx,ty); }
給定區間(1,n),定義f(i,j)爲區間(i,j)上的最大值,g(i,j)爲從左往右遍歷該區間上最大值的更新次數,求\(\Sigma f(i,i+m)和\Sigma g(i,i+m),(1\le i,i+m \le n)\)隊列
解法:
最大值同上能夠用座標遞增值遞減的單調隊列的隊首元素表示,而更新次數能夠用座標遞減值遞減的單調隊列的大小來表示,發現最大值也能夠用後者的來維護,所以只須要從右往左創建遞減的單調隊列便可ci
#include <iostream> #include <cstdio> using namespace std; typedef long long ll; const int maxn=1e7+5; ll a[maxn],q1[maxn],q2[maxn]; ll ans1[maxn],ans2[maxn]; int main(){ ll T,n,m,k,p,q,r,mod; scanf("%lld",&T); while(T--){ scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&m,&k,&p,&q,&r,&mod); for(int i=1;i<=k;i++) scanf("%lld",&a[i]); for(int i=k+1;i<=n;i++) a[i]=(p*a[i-1]+q*i+r)%mod; ll res1=0,res2=0; ll l=n+1,r=n; for(int i=n;i>=1;i--){ while(r>=l&&a[q2[l]]<=a[i]) l++; q2[--l]=i; while(r>=l&&q2[r]>i+m-1) r--; if(i<=n-m+1){ res2+=(r-l+1)^i; res1+=a[q2[r]]^i; } } printf("%lld %lld\n",res1,res2); } }
給定矩陣M[n][m],a,b,對於M中每一個a*b的子矩陣,求出子矩陣中的最小元素並求和get
解法:
遍歷每一行,從左至右存儲每一個長度爲b的區間內的最小數,獲得新矩陣B[n][m-b+1],遍歷B的每一列,一樣用單調遞增隊列就能夠維護獲得每一個子矩陣的最小值。io
#include <iostream> using namespace std; typedef long long ll; const int maxn=3e3+5; int h[maxn][maxn],g[maxn*maxn],q[maxn]; int a1[maxn][maxn]; int main(){ int n,m,a,b,x,y,z; scanf("%d%d%d%d",&n,&m,&a,&b); scanf("%d%d%d%d",&g[0],&x,&y,&z); for(int i=1;i<=n*m-1;i++) g[i]=((ll)g[i-1]*(ll)x%z+(ll)y)%z; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) h[i][j]=g[(i-1)*m+j-1]; for(int i=1;i<=n;i++){//算出每一個橫區間的最小值矩陣 int l=1,r=0; for(int j=1;j<=m;j++){ while(r>=l&&h[i][q[r]]>h[i][j]) r--;//不嚴格增(最小值) q[++r]=j; while(r>=l&&j-q[l]+1>b)l++; if(j>=b) a1[i][j]=h[i][q[l]]; } } ll ans=0; for(int j=b;j<=m;j++){ int l=1,r=0; for(int i=1;i<=n;i++){ while(r>=l&&a1[q[r]][j]>a1[i][j]) r--; q[++r]=i; while(r>=l&&i-q[l]+1>a)l++; if(i>=a) ans+=a1[q[l]][j]; } } printf("%lld\n",ans); }
給定一個n*n的矩陣和每一個位置上的權值 ai,j ,求最大的子矩陣,知足子矩陣中最大值和最小值之差不超過m。模板
解法:
枚舉子矩陣的上邊與下邊O(N^2),存儲區間內n條寬度爲1的矩陣的最小值和最大值,在下界遞增的時候最小值與最大值的維護是O(1)的,每條矩陣抽象成了一個點(有兩個值),此時能夠用單調隊列在O(N)內求出最大值最小值相差不超過m的最大區間。
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const int maxn=505; int a[maxn][maxn]; int maxx[maxn],minn[maxn]; int q1[maxn],q2[maxn]; int main(){ int T; scanf("%d",&T); while(T--){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); int ans=0; for(int t=1;t<=n;t++){//枚舉上界 for(int i=1;i<=n;i++)//初始化最大最小值 minn[i]=maxx[i]=a[t][i]; for(int b=t;b<=n;b++){//枚舉下界 if(b!=t){ for(int i=1;i<=n;i++){ minn[i]=min(minn[i],a[b][i]); maxx[i]=max(maxx[i],a[b][i]); } } int l1=1,r1=0,l2=1,r2=0,now=1; for(int i=1;i<=n;i++){ while(r1>=l1 && maxx[q1[r1]]<maxx[i]) r1--;//不嚴格遞減(最大值) while(r2>=l2 && minn[q2[r2]]>minn[i]) r2--;//不嚴格遞增(最小值) q1[++r1]=i; q2[++r2]=i; while(r1>=l1&&r2>=l2&&maxx[q1[l1]]-minn[q2[l2]]>m){ if(q1[l1]<q2[l2])now=q1[l1++]+1; else now=q2[l2++]+1; } ans=max(ans,(i-now+1)*(b-t+1)); } } } printf("%d\n",ans); } }