1. 程式人生 > 其它 >在安卓手機上的便籤中怎麼列出一天的事情,並且標記完成呢?

在安卓手機上的便籤中怎麼列出一天的事情,並且標記完成呢?

題意

​ 一個長度為 \(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 ;	}