zoukankan      html  css  js  c++  java
  • JZOJ 6860. 【2020.11.14提高组模拟】鬼渊传说(前缀和+指针)

    JZOJ 6860. 【2020.11.14提高组模拟】鬼渊传说

    题解

    • 这题要用到图论中的欧拉定理,即 V − E + F = 2 V-E+F=2 VE+F=2,其中 V V V为点数, E E E为边数, F F F为面数(包括整个图形以外的部分),可以用归纳法证明,对任意连通平面图成立。
    • 那么在这题里把黑格看成点,可以通过判断欧拉定理是否成立来判断是否只有一个连通块。
    • 设四元环(相邻的四格构成的正方形)个数为 S S S,显而易见 F = S + 1 F=S+1 F=S+1,因为网格图中的面只能是四元环(除了整个图形以外的部分),则有 V − E + S = 1 V-E+S=1 VE+S=1
    • 可以分别预处理好点、横向边、纵向边、四元环个数前缀和, V − E + S V-E+S VE+S的值可以用前缀和计算得出。注意不同的前缀和式子略有不同,所有需要把横向边和纵向边拆开算。
    • 先忽略中间的空腔,可以先枚举上下边界,然后枚举右边界时,计算出左边界的前缀和值应为多少,用桶维护,在桶里查找对应的值加入答案。
    • 接着把空腔考虑进来,可以先BFS一遍求出所有空腔的上下左右边界 x 0 , x 1 , y 0 , y 1 x0,x1,y0,y1 x0,x1,y0,y1
    • 后面的计算过程中,枚举每个上边界 i i i时, 把所有 x 0 > i x0>i x0>i的空腔记录到 y 1 y1 y1的位置,确定下边界后,可以从左往右扫一遍,依次求出每个 k k k作为右边界时, 左边界可到的最小值 d k d_k dk d k d_k dk是单调不下降的。
    • 最后从右往左枚举右边界,用指针 l , r l,r l,r记录可作为左边界的区间,然后不断移动指针修改桶里的值。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 310
    int a[N][N], f[N][N], g0[N][N], g1[N][N], h[N][N];
    int vi[N][N], fx[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    int last[N], nxt[N * N], to[N * N], li[N * N], len;
    int s[N * N], bz[N * N], p[N], d[N];
    int x0, x1, y0, y1, n, m;
    struct {
    	int x0, y0, x1, y1;
    }c[N * N];
    void dfs(int x, int y) {
    	x0 = min(x, x0), y0 = min(y, y0);
    	x1 = max(x, x1), y1 = max(y, y1);
    	vi[x][y] = 1;
    	for(int i = 0; i < 4; i++) {
    		int xx = x + fx[i][0], yy = y + fx[i][1];
    		if(xx < 1 || xx > n || yy < 1 || yy > m || vi[xx][yy] || a[xx][yy]) continue;
    		dfs(xx, yy);
    	}
    }
    void add(int x, int y, int z) {
    	to[++len] = y;
    	li[len] = z;
    	nxt[len] = last[x];
    	last[x] = len;
    }
    int main() {
    	int i, j, k, l, r;
    	char ch;
    	scanf("%d%d
    ", &n, &m);
    	for(i = 1; i <= n; i++) 
    	{
    		for(j = 1; j <= m; j++) scanf("%c", &ch), a[i][j] = ch - 48;
    		scanf("
    ");
    	}
    	int tot = 0;
    	for(i = 2; i < n; i++) {
    		for(j = 2; j < m; j++) if(!vi[i][j] && !a[i][j]) {
    			x0 = y0 = m + n;
    			x1 = y1 = -1;
    			dfs(i, j);
    			if(x0 == 1 || y0 == 1 || x1 == n || y1 == m) continue;
    			c[++tot] = {x0, y0, x1, y1};
    		}
    	}
    	for(i = 1; i <= n; i++) {
    		for(j = 1; j <= m; j++) {
    			f[i][j] = f[i][j - 1] + f[i - 1][j] - f[i - 1][j - 1] + (a[i][j] == 1);
    			g0[i][j] = g0[i][j - 1] + g0[i - 1][j] - g0[i - 1][j - 1] + (a[i][j - 1] && a[i][j]);
    			g1[i][j] = g1[i][j - 1] + g1[i - 1][j] - g1[i - 1][j - 1] + (a[i - 1][j] && a[i][j]);
    			h[i][j] = h[i][j - 1] + h[i - 1][j] - h[i - 1][j - 1] + (a[i][j] && a[i][j - 1] && a[i - 1][j] && a[i - 1][j - 1]);
    		}
    	}
    	int id = 0, ans = 0, t;
    	for(i = 1; i <= n; i++) {
    		len = 0;
    		memset(last, 0, sizeof(last));
    		for(j = 1; j <= tot; j++) if(i < c[j].x0) add(c[j].y1, c[j].x1, c[j].y0);
    		for(j = i; j <= n; j++) {
    			d[0] = 0;
    			for(k = 1; k <= m; k++) {
    				d[k] = d[k - 1];
    				for(l = last[k - 1]; l; l = nxt[l]) if(to[l] < j) d[k] = max(d[k], li[l] - 1);
    			}
    			id++;
    			for(k = 0; k <= m; k++)	p[k] = f[j][k] - f[i - 1][k] - g0[j][k + 1] + g0[i - 1][k + 1] - g1[j][k] + g1[i][k] + h[j][k + 1] - h[i][k + 1] + 1;
    			l = m, r = m - 1;
    			for(k = m; k; k--) {
    				t = f[j][k] - f[i - 1][k] - g0[j][k] + g0[i - 1][k] - g1[j][k] + g1[i][k] + h[j][k] - h[i][k];
    				while(l > d[k]) {
    					l--;
    					if(bz[p[l]] < id) bz[p[l]] = id, s[p[l]] = 0;
    					s[p[l]]++;
    				}
    				while(r >= k) s[p[r]]--, r--;
    				if(bz[t] == id) ans += s[t];
    			}
    		}
    	}
    	printf("%d", ans);
    	return 0;
    }
    
  • 相关阅读:
    测试工程师入门要了解什么?(四)
    测试工程师入门要了解什么?(三)
    测试工程师入门要了解什么?(二)
    测试工程师入门要了解什么?(一)
    测试工程师专业术语
    测试场景标准库
    猜数字
    遍历
    python基础
    python class1
  • 原文地址:https://www.cnblogs.com/LZA119/p/14279485.html
Copyright © 2011-2022 走看看