• [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}^jlbrace U_{i,d} brace ]

    这条式子中,相当于枚举了(i,(k,j))为底,统计向上延伸的最长长度

    这个式子可以用单调栈在线性时间内求解,其过程可以描述为

    1.每次插入元素(U_{i,j}),得到它的影响区间(kin [L,j])

    2.将原先单调栈内(kin [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
    ",ans%10007);
    }
    
    
    
    
  • 相关阅读:
    隐藏PHP程序头部发出的:XPoweredBy: PHP/5.2.4类似的信息
    wtai无线应用简单方法
    七大你可能不知道的 Chrome 使用技巧
    常用JS图片滚动(无缝、平滑、上下左右滚动)
    精心挑选12款优秀的 JavaScript 日历和时间选择插件
    JS动态加载JS文件与CSS文件
    通用的JS表单验证插件代码
    dede(织梦)CMS后台的验证码不显示
    Sublime Text2 使用方法及快捷键
    Meta标签详解
  • 原文地址:https://www.cnblogs.com/chasedeath/p/13549652.html
Copyright © 2020-2023  润新知