zoukankan      html  css  js  c++  java
  • 【简解】C2CRNI

    【题目大意】

    给定一个N行N列的矩阵,每个格子要么为白色要么为黑色。黑矩形为所涵单元格数大于等于2且所涵单元格均为黑色的矩表。要解决的问题是在给定的矩形中找出两个没有共公部分的黑矩形,输出所有方案数,由于数较大,输出它对10007的余数。

    传送门

    【分析】

    听说是道很老的套路题,考试考了这道。。。

    有个比较明显的东西,就是我们要求的两个黑矩形没有公共部分,那么必然会有一条竖线(或横线)把这两个黑矩形分开,我们就可以枚举这条竖线(横线)来统计答案。

    为了避免计算重复,我们需要一边固定,另一边统计总和。并且,还应减去既能被一条竖线分开又能被一条横线分开的两个黑矩形(因为它被算了两次嘛)。

    大体思路就是这样,然后就是实现。

    比较容易想到的就是计算以每个格子为左下角(右下角、左上角、右上角)的黑矩形有多少个。

    考试的时候太弱了,只会写(O(n^4))的暴力。。。(它还挂了)

    简单说一点暴力吧,就是(O(n^2))枚举每个格子,然后(O(n^2))枚举矩形的长度,可以用二维前缀和(O(1))来判断枚举的矩形是否合法,然后就(O(n^4))预处理,再(O(n^2))统计贡献。

    可以看到,预处理是瓶颈,我们应考虑如何优化预处理的复杂度。

    对每一行拆开考虑,我们会发现比较像直方图里的最大矩形,然后就做啊。

    我们以计算以每个格子为右下角的黑矩形有多少个为例。

    (f[i][j])就是以((i,j))为右下角的黑矩形有多少个,我们会发现,它可以直接继承它前面出现的第一个小于它高度的位置的答案,那中间那些比它还高的矩形呢,没错,只能扩展它自己的高度。

    (f[i][j]=f[i][k]+1+h[j]*(j-k)-1)

    其中(k)表示从(j)向前走第一次出现的(h[k]<h[j])的位置,那么直接可以继承(f[i][k]),然后加一,然后对于那块大的矩形里的所有点均可作为当前矩形的左上角,除了自己本身,因为(1 imes 1)的黑矩形不能算入答案,所以会减一。

    考虑到(k)的计算,想到单调栈优化,可以做到(O(1))转移。

    所以我们做到了(O(n^2))预处理,然后就行了。

    细节有点多,还有要注意时刻取模。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    const int P = 10007;
    const int N = 1000 + 5; 
    inline int read(){
    	int f = 1, x = 0; char ch;
    	do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
    	do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
    	return f * x;
    }
    inline void hand_in() {
    	freopen("crni.in", "r", stdin);
    	freopen("crni.out", "w", stdout);
    }
    int n, mp[N][N], res;
    int f[N][N], g[N][N], h[N];
    char ch[N][N]; int sta[N], top;
    int main(){
    //	hand_in();
    	n = read();
    	for (int i = 1;i <= n; ++i) scanf("%s", ch[i] + 1);
    	for (int i = 1;i <= n; ++i) {
    		for (int j = 1;j <= n; ++j) {
    			mp[i][j] = (ch[i][j] == 'C');
    		}
    	}
    	// 左上 
    	memset(h, 0, sizeof h);
    	for (int i = 1;i <= n; ++i) {
            top = 0;
            sta[top] = 0;
            for (int j = 1;j <= n; ++j) {
                if (mp[i][j] == 1) h[j] ++;
                else h[j] = 0;
                if (h[j] == 0) {
                	top = 0;
                	sta[top] = j;
                	continue;
                }
                int size;
    			while (top && h[sta[top]] >= h[j]) top --;
    			size = h[j] * (j - sta[top]) - 1;
    			f[i][j] = f[i][sta[top]] + size + (top > 0);
                f[i][j] %= P;
                sta[++top] = j;
            }
        }
    	//右下 
    	memset(h, 0, sizeof h);
    	for (int i = n;i >= 1; --i) {
            top = 0;
            sta[top] = n + 1;
            for (int j = n;j >= 1; --j) {
                if (mp[i][j] == 1) h[j] ++;
                else h[j] = 0;
                if (h[j] == 0) {
                	top = 0;
                	sta[top] = j;
                	continue;
                }
                int size;
    			while (top && h[sta[top]] >= h[j]) top --;
    			size = h[j] * (sta[top] - j) - 1;
    			g[i][j] = g[i][sta[top]] + size + (top > 0);
                g[i][j] %= P;
                sta[++top] = j;
            }
    	}
    	int suma = 0, sumb = 0;
    	for (int i = 1;i < n; ++i) {
    		for (int j = 1;j <= n; ++j) {
    			suma += f[i][j], sumb += g[i + 1][j];
    			suma %= P, sumb %= P;
    		}
    		res += suma * sumb;
    		res %= P;
    		sumb = 0;
    	}
    	suma = 0, sumb = 0;
    	for (int j = 1;j < n; ++j) {
    		for (int i = 1;i <= n; ++i) {
    			suma += f[i][j], sumb += g[i][j + 1];
    			suma %= P, sumb %= P;
    		}
    		res += suma * sumb;
    		res %= P;
    		sumb = 0;
    	}
    	for (int i = 1;i <= n; ++i) {
    		for (int j = 1;j <= n; ++j) {
    			f[i][j] += f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + P;
    			f[i][j] %= P;
    		}
    	}
    	for (int i = 1;i < n; ++i) {
    		for (int j = 1;j < n; ++j) {
    			res -= (f[i][j] * g[i + 1][j + 1] + P) % P;
    			while (res < 0) res += P;
    		}
    	}
    	//右上 
    	memset(f, 0, sizeof f);
    	memset(h, 0, sizeof h);
    	for (int i = 1;i <= n; ++i) {
            top = 0;
            sta[top] = n + 1;
            for (int j = n;j >= 1; --j) {
                if (mp[i][j] == 1) h[j] ++;
                else h[j] = 0;
                if (h[j] == 0) {
                	top = 0;
                	sta[top] = j;
                	continue;
                }
                int size;
    			while (top && h[sta[top]] >= h[j]) top --;
    			size = h[j] * (sta[top] - j) - 1;
    			f[i][j] = f[i][sta[top]] + size + (top > 0);
                f[i][j] %= P;
                sta[++top] = j;
            }
        }
        //左下 
    	memset(g, 0, sizeof g);
    	memset(h, 0, sizeof h);
    	for (int i = n;i >= 1; --i) {
            top = 0;
            sta[top] = 0;
            for (int j = 1;j <= n; ++j) {
                if (mp[i][j] == 1) h[j] ++;
                else h[j] = 0;
                if (h[j] == 0) {
                	top = 0;
                	sta[top] = j;
                	continue;
                }
                int size;
    			while (top && h[sta[top]] >= h[j]) top --;
    			size = h[j] * (j - sta[top]) - 1;
    			g[i][j] = g[i][sta[top]] + size + (top > 0);
                g[i][j] %= P;
                sta[++top] = j;
            }
    	}
        for (int i = 1;i <= n; ++i) {
        	for (int j = n;j >= 1; --j) {
        		f[i][j] += f[i - 1][j] + f[i][j + 1] - f[i - 1][j + 1] + P;
        		f[i][j] %= P;
        	}
        }
    	for (int i = 1;i < n; ++i) {
    		for (int j = 2;j <= n; ++j) {
    			res -= (f[i][j] * g[i + 1][j - 1] + P) % P;
    			while (res < 0) res += P;
    		}
    	}
    	printf("%d", res);
    	return 0;
    }
    
  • 相关阅读:
    什么是内存泄漏,为什么会导致内存溢出?
    深入了解Redis(1)字符串底层实现
    深入了解Redis(3)对象
    201871010135 张玉晶《面向对象程序设计(java)》第十一周学习总结
    201871010135 张玉晶 《面向对象程序设计(java)》第二周学习总结
    201871010135 张玉晶《面向对象程序设计(java)》第十二周学习总结
    20187101035 张玉晶《面向对象程序设计(java)》第八周学习总结
    201871010135 张玉晶 《2019面向对象程序设计(java)课程学习进度条》
    201871010135张玉晶《面向对象程序设计(Java)》第四周学习总结
    201871010135 张玉晶《面向对象程序设计(java)》第七周学习总结
  • 原文地址:https://www.cnblogs.com/silentEAG/p/11635702.html
Copyright © 2011-2022 走看看