1. 程式人生 > 實用技巧 >[COCI2010-2011#2] CRNI(單調棧)

[COCI2010-2011#2] CRNI(單調棧)

[COCI2010-2011#2] CRNI(單調棧)

問題分析

首先考慮兩個不相交的矩形可能存在的位置關係,我將其分成

1.左右

2.上下

3.左上右下

4.左下右上

發現1,2,3,4之間有相交,考慮四種情況的答案應該是1+2-3-4

統計方法

核心: 統計以一個點作為頂點的矩形數量

以統計\(i,j\)為右下角的矩形為例,先不考慮矩形大小>1的限制

顯然可以線上性時間內處理得到每個\(i,j\)向上連續延伸的連續1長度,設其為\(U_{i,j}\)

假設枚舉了\(i\),從左到右依次掃描\(j\),則得到\(i,j\)位置的答案應該是

\[\sum_{k=1}^{j} min_{d=k}^j\lbrace U_{i,d}\rbrace \]

這條式子中,相當於枚舉了\(i,(k,j)\)為底,統計向上延伸的最長長度

這個式子可以用單調棧線上性時間內求解,其過程可以描述為

1.每次插入元素\(U_{i,j}\),得到它的影響區間\(k\in [L,j]\)

2.將原先單調棧內\(k\in [L,j]\)這段區間的答案減掉,改為\(U_{i,j}\cdot (j-L+1)\)

類似的,可以通過改變迴圈順序和額外記錄向下延伸的長度\(D_{i,j}\)來統計四種頂點的答案(詳細見程式碼)

然後可以用字首和幫助統計以上4種答案,列舉一個端點,另一個查詢字首和即可

tips: 注意累和順序,字首和要開long long

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
template <class T=int> T rd(){
	T s=0; int f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e3+10;

int n;
char a[N][N];
int D[N][N],U[N][N]; //i,j向下/上延伸的最長長度
int stk[N],c[N],top;
int CRR[N][N]; // 以i,j為右下角的矩形個數
int CLL[N][N]; // 以i,j為左上角的矩形個數
int CLR[N][N]; // 以i,j為右上角的矩形個數
int CRL[N][N]; // 以i,j為左下角的矩形個數
ll SLL[N][N],SRL[N][N]; // 字首和

int main(){
	n=rd();
	rep(i,1,n) scanf("%s",a[i]+1);
	rep(i,1,n) rep(j,1,n) if(a[i][j]=='C') U[i][j]=U[i-1][j]+1;
	drep(i,n,1) rep(j,1,n) if(a[i][j]=='C') D[i][j]=D[i+1][j]+1;
	rep(i,1,n) {
		// 統計四種端點的情況
		top=0;
		int now=0;
		rep(j,1,n) {
			int x=U[i][j],cnt=1;
			while(top && stk[top]>=x) cnt+=c[top],now-=c[top]*stk[top],top--;
			stk[++top]=x,c[top]=cnt; now+=x*cnt;
			CRR[i][j]=max(now-1,0);
		}

		now=top=0;
		rep(j,1,n) {
			int x=D[i][j],cnt=1;
			while(top && stk[top]>=x) cnt+=c[top],now-=c[top]*stk[top],top--;
			stk[++top]=x,c[top]=cnt; now+=x*cnt;
			CLR[i][j]=max(now-1,0);
		}

		now=top=0;
		drep(j,n,1) {
			int x=U[i][j],cnt=1;
			while(top && stk[top]>=x) cnt+=c[top],now-=c[top]*stk[top],top--;
			stk[++top]=x,c[top]=cnt; now+=x*cnt;
			CRL[i][j]=max(now-1,0);
		}

		now=top=0;
		drep(j,n,1) {
			int x=D[i][j],cnt=1;
			while(top && stk[top]>=x) cnt+=c[top],now-=c[top]*stk[top],top--;
			stk[++top]=x,c[top]=cnt; now+=x*cnt;
			CLL[i][j]=max(now-1,0);
		}
	}

	drep(i,n,1) drep(j,n,1) SLL[i][j]=SLL[i+1][j]+SLL[i][j+1]-SLL[i+1][j+1]+CLL[i][j];
	rep(i,1,n) drep(j,n,1) SRL[i][j]=SRL[i-1][j]+SRL[i][j+1]-SRL[i-1][j+1]+CRL[i][j];
	// 字首和

	ll ans=0;
	rep(i,1,n) rep(j,1,n) if(CRR[i][j]) ans+=CRR[i][j]*(SLL[i+1][1]+SLL[1][j+1]-SLL[i+1][j+1]);
	rep(i,1,n) rep(j,1,n) ans-=CLR[i][j]*SRL[i-1][j+1];
	// 統計4種情況
	printf("%lld\n",ans%10007);
}