Harbor映象倉庫的搭建使用
題意
一個長度為 \(n\) 的排列 \(a\),求這個排列中有多少個區間 \([l,r]\),滿足這個區間的 \(max-min=r-l\)。
資料範圍:\(n \le 3 \times 10 ^ 5\)。
題解
考慮分治做法。
考慮分治區間 \([le,rig]\),我們現在只考慮經過 \(mid\) 的那部分割槽間,其餘的遞迴解決。遞迴邊界 \(le==rig\),答案直接 \(+1\)。
先預處理 \(mx(max)\) 和 \(mn(min)\) 陣列,維護最大最小值的字首。具體定義如下:
- \(mx[mid]=mn[mid]=a[mid],mx[mid+1]=mn[mid+1]=a[mid+1]\)
- \(mx[i]=max(mx[i+1],a[i]),mn[i]=min(mn[i+1],a[i]),(le \le i < mid)\)
- \(mx[i]=max(mx[i-1],a[i]),mn[i]=min(mn[i+1],a[i]),(mid+1\le i\le rig)\)
可以發現,\(mx\) 和 \(mn\) 陣列就是以 \(mid\) 為分界,向左和向右延伸的字首和。
假定我們枚舉了左端點 \(i\) 和右端點 \(j\)。
因為 \(max-min=j-i\),我們根據 \(max\) 和 \(min\) 在 \(mid\) 左邊還是右邊來分類討論。
情況1:\(mx[i]>mx[j]\and mn[i]<mn[j],mx[i]-mn[i]=j-i\)
我們發現最大值和最小值都在左邊,我們可以直接列舉左端點 \(i\),\(i\) 確定之後 \(j=mx[i]-mn[i]+i\),\(j\) 也就確定了,再判斷那個 \(j\) 是否合法(超界?\(mx\) 或 \(mn\) 不滿足?)就行了。
情況2:\(mx[i]<mx[j]\and mn[i]>mn[j],mx[j]-mn[j]=j-i\)
與情況 \(1\) 類似,列舉右端點 \(j\),判斷對應的i是否合法即可。
情況3:\(mx[i]<mx[j]\and mn[i]<mn[j],mx[j]-mn[i]=j-i\)
式子移項可得:\(mx[j]-j=mn[i]-i\)
這種情況相對麻煩一些。考慮列舉左端點 \(i\),對於每個 \(i\) 找出所有滿足上述不等式的 \(j\),只要滿足不等式,用桶可以方便維護。
所以值可以方便維護,問題轉化為了如何維護滿足不等式關係。
考慮這幾個值的單調性。因為 \(mx\) 陣列都是單增的,\(mn\) 陣列都是單減的,所以隨著 \(i\) 變小滿足 \(mn[i]<mn[j]\) 的 \(j\) 區間的右邊界 一定會從 \(mid+1\) 到 \(rig\) 逐漸右移,就是說某個區間 \([mid+1,r]\) 滿足 \(mn\) 的關係式,並且隨著 \(i\) 變小 \(r\) 會右移。
再考慮 \(mx[i]<mx[j]\) 這一條件,隨著 \(i\) 變小,\(mx[i]\) 會逐漸變大,又因為 \(mx[j]\) 也是單增的,比較靠左的 \(j\) 會逐漸不滿足條件,被剔除貢獻。發現貢獻滿足先入後出的性質。
所以我們列舉左端點 \(i\) ,對於右端點維護一個佇列,根據兩個不等式加入和彈出。
情況4:\(mx[i]>mx[j]\and mn[i]>mn[j],mx[i]-mn[j]=j-i\)
式子轉化為:\(mx[i]+i=mn[j]+j\)
大概和情況 \(3\) 類似,只是式子有所變化,佇列頭尾所用的不等式有所交換。
程式碼及細節
注意情況 \(3\) 和情況 \(4\) 結束之後要清空桶,注意情況 \(1\) 和 \(2\) 判斷合法不要漏掉條件。
#include <bits/stdc++.h>
using namespace std ;
#define ll long long
#define rep(i,l,r) for(ll i=(l);i<=(r);++i)
#define per(i,r,l) for(ll i=(r);i>=(l);--i)
#define wif while
#define mem(a,b) memset(a,b,sizeof a)
const ll inf = INT_MAX , df = 3e5 + 7 , N = 1e6 + 7 ;
ll i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a[df],ans,mx[df],mn[df],buc[N*3];
void solve(ll le,ll rig) {
if( le > rig ) return ;
ll mid = (le+rig) >> 1 ;
if( le == rig ) return ans ++ , void() ;
solve( le , mid ) , solve( mid + 1 , rig ) ;
mx[mid] = mn[mid] = a[mid] , mx[mid+1] = mn[mid+1] = a[mid+1] ;
rep(i,mid+2,rig) mx[i] = max( mx[i-1] , a[i] ) , mn[i] = min( mn[i-1] , a[i] ) ;
per(i,mid-1,le) mx[i] = max( mx[i+1] , a[i] ) , mn[i] = min( mn[i+1] , a[i] ) ;
per(i,mid,le) {
ll j = mx[i] - mn[i] + i ;
if( mx[i] > mx[j] && mn[i] < mn[j] && j > mid && j <= rig ) ans ++ ; }
rep(j,mid+1,rig) {
ll i = j - mx[j] + mn[j] ;
if( mx[j] > mx[i] && mn[j] < mn[i] && i <= mid && i >= le ) ans ++ ; }
ll j = mid + 1 , k = mid ;
per(i,mid,le) {
wif(mn[i]<mn[k+1]&&k+1<=rig) k ++ , buc[mx[k]-k+N] ++ ;
wif(mx[i]>mx[j]&&j<=k) buc[mx[j]-j+N] -- , j ++ ;
ans += buc[mn[i]-i+N] ; }
wif(j<=k) buc[mx[j]-j+N] -- , j ++ ;
j = mid + 1 , k = mid ;
per(i,mid,le) {
wif(mx[i]>mx[k+1]&&k+1<=rig) k ++ , buc[mn[k]+k+N] ++ ;
wif(mn[i]<mn[j]&&j<=k) buc[mn[j]+j+N] -- , j ++ ;
ans += buc[mx[i]+i+N] ; }
wif(j<=k) buc[mn[j]+j+N] -- , j ++ ;
return ; }
inline ll read() {
ll x = 0 , y = 1 ; char ch = getchar() ;
wif( ch > '9' || ch < '0' ) y = ( ch == '-' ) ? - 1 : 1 , ch = getchar() ;
wif( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar() ;
return x * y ; }
int main() {
n = read() ;
rep(i,1,n) x = read() , y = read() , a[x] = y ;
solve(1,n) ;
printf("%lld\n",ans); return 0 ; }