1. 程式人生 > 實用技巧 >【CF526F】Pudding Monsters(分治套路題)

【CF526F】Pudding Monsters(分治套路題)

點此看題面

  • 一個\(n\times n\)的正方形,其中每行每列恰有一個點。
  • 問有多少子正方形,也滿足每行每列恰有一個點。
  • \(n\le3\times10^5\)

二維化一維

顯然這道題很容易想到降維。

既然每行只有一個點,我們可以用\(a_i\)表示第\(i\)行的點在第幾列。

現在問題就變成有多少區間滿足其中元素排序之後值連續。

則區間\([l,r]\)合法的充要條件就是\(\max_{i=l}^ra_i-\min_{i=l}^ra_i=r-l\)

分治套路題

對於當前分治區間\([l,r]\),設其中點為\(mid\),那麼\([l,mid]\)\([mid+1,r]\)兩區間各自的答案可以直接遞迴處理。

接著我們先預處理出\(Mx,Mn\)兩個陣列,當\(i\in[l,mid]\)時表示\([i,mid]\)的最值,當\(i\in[mid+1,r]\)時表示\([mid+1,i]\)的最值。

現在我們就要考慮如何統計跨中點的區間的答案,分成兩類情況。

兩最值在中點同側

假設在\([l,mid]\)中。

我們列舉左端點\(i\),此時最值為\(Mx_i,Mn_i\),所以滿足\(Mx_i-Mn_i=j-i\),即\(j=Mx_i-Mn_i+i\)

那麼我們只要驗證一下\(j\)是不是一個合法的右端點即可。

兩最值在中點異側

假設最小值在\([l,mid]\)中,最大值在\([mid+1,r]\)

中。

我們從大到小列舉\(i\),同時開\(p,q\)兩個指標維護出區間\([p,q]\)表示合法右端點\(j\)的取值區間,顯然這東西隨著\(i\)的移動是單調右移的。

此時最值為\(Mx_j,Mn_i\),故\(Mx_j-Mn_i=j-i\),即\(Mn_i-i=Mx_j-j\)

那麼只要開桶記一下\(Mx_j-j=x\)\(j\)的個數,就可以了。

程式碼:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300000
#define LL long long
using namespace std;
int n,a[N+5];LL ans;
int Mx[N+5],Mn[N+5],c[2*N+5];I void Solve(CI l,CI r)//分治
{
	if(l==r) return (void)++ans;RI i,p,q,mid=l+r>>1;Solve(l,mid),Solve(mid+1,r);//遞迴子區間
	Mx[mid]=Mn[mid]=a[mid],Mx[mid+1]=Mn[mid+1]=a[mid+1];
	for(i=mid-1;i>=l;--i) Mx[i]=max(Mx[i+1],a[i]),Mn[i]=min(Mn[i+1],a[i]);//預處理左區間字尾最值
	for(i=mid+2;i<=r;++i) Mx[i]=max(Mx[i-1],a[i]),Mn[i]=min(Mn[i-1],a[i]);//預處理右區間字首最值
	for(i=l;i<=mid;++i) p=i+Mx[i]-Mn[i],mid<p&&p<=r&&Mn[i]<Mn[p]&&Mx[p]<Mx[i]&&++ans;//最值都在左區間
	for(i=mid+1;i<=r;++i) p=i-Mx[i]+Mn[i],l<=p&&p<=mid&&Mn[i]<Mn[p]&&Mx[p]<Mx[i]&&++ans;//最值都在右區間
	for(i=mid,p=q=mid+1;i>=l;ans+=c[Mn[i]-i+n],--i)//最小值在左區間,最大值在右區間
		{W(q<=r&&Mn[q]>Mn[i]) ++c[Mx[q]-q+n],++q;W(p^q&&Mx[p]<Mx[i]) --c[Mx[p]-p+n],++p;}//p,q維護合法右端點區間
	W(p^q) --c[Mx[p]-p+n],++p;//清空
	for(i=mid,p=q=mid+1;i>=l;ans+=c[Mx[i]+i],--i)//最大值在左區間,最小值在右區間
		{W(q<=r&&Mx[q]<Mx[i]) ++c[Mn[q]+q],++q;W(p^q&&Mn[p]>Mn[i]) --c[Mn[p]+p],++p;}
	W(p^q) --c[Mn[p]+p],++p;//清空
}
int main()
{
	RI i,x,y;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&x,&y),a[x]=y;//二維化一維
	return Solve(1,n),printf("%lld\n",ans),0;
}