[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);
}



posted @ 2020-08-23 16:47  chasedeath  阅读(152)  评论(0编辑  收藏  举报