【TCO2013 Semifinal 2】 OneBlack

10 篇文章 0 订阅
6 篇文章 0 订阅
Description

一个   的网格图,一些格子有障碍。一条合法路径的定义是从   到  的,一共走   步的路径。
你要把一些格子染黑,使得每一条合法路径上恰好有一个黑点。问合法方案数。

Difficulty

MainAlgorithm

对偶图
DP

Complexity

Solution

首先我们把能从   到的、能到   的点抠出来。其余的点都是自由的,给答案乘   即可。
我们观察这些点,他们构成了一个平面图DAG。
考虑一下染色的意义,即一个极小点割。即这个割集中的每一个点都有用而不可删除。
那么,我们把原图转对偶图,在左下角建立S,在右上角建立T,一个合法的方案即为从S到T的一条路径。

DP统计一下就好。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
const int N = 1005, M = 45, S = 1001, T = 1002, mod = 1000000007;
class OneBlack {
public:
	char a[N][N]; bool vis[M][M], v1[M][M], v2[M][M], ve[N][N], Ch[M][M];
	LL f[N], num = 1; int in[N], n, m, pos[N], sz, hd, tl, que[N], c[M][M], col, cl, b[N][N];
	struct Edge { int y, nex; } g[N * N];
	void Init(int x, int y) {
		if (x == y || ve[x][y]) return ;
		g[++ sz] = (Edge) { y, pos[x] }, pos[x] = sz;
		in[y] ++, ve[x][y] = 1;
	}
	void Find(int x, int y) {
		if (x > n || y > m || vis[x][y] || c[x][y]) return ;
		c[x][y] = col;
		Find(x + 1, y), Find(x, y + 1), Find(x + 1, y + 1);
	}
	void Dfs(int x, int y) {
		if (!x || y > m) col = S;
		if (x > n || !y) col = T;
		if (col == S || col == T || vis[x][y] || Ch[x][y]) return ;
		Ch[x][y] = 1;
		Dfs(x + 1, y), Dfs(x - 1, y), Dfs(x, y - 1), Dfs(x, y + 1);
	}
	int countColorings(vector <string> grid) {
		n = grid.size();
		m = grid[0].size();
		Rep(i, 1, n) {
			Rep(j, 1, m) a[i][j] = grid[i - 1][j - 1];
		}
		v1[1][0] = 1, v2[n + 1][m] = 1;
		Rep(i, 1, n) {
			Rep(j, 1, m) if (a[i][j] != '#') {
				v1[i][j] = v1[i - 1][j] | v1[i][j - 1];
				vis[i][j] = v1[i][j];
			}
		}
		Dwn(i, n, 1) {
			Dwn(j, m, 1) if (a[i][j] != '#') {
				v2[i][j] = v2[i + 1][j] | v2[i][j + 1];
				vis[i][j] &= v2[i][j];
			}
		}
		Rep(i, 1, n) {
			Rep(j, 1, m) if (a[i][j] != '#' && !vis[i][j]) (num *= 2) %= mod;
		}
		Rep(i, 1, n) {
			Rep(j, 1, m) {
				if (!c[i][j] && !vis[i][j]) col = (++ cl), Dfs(i, j), Find(i, j);
				else if (!c[i][j]) b[i][j] = ++ cl;
			}
		}
		Rep(i, 1, n) c[i][0] = T, c[i][m + 1] = S;
		Rep(i, 1, m) c[0][i] = S, c[n + 1][i] = T;
		Rep(i, 1, n) {
			Rep(j, 1, m) {
				int x = c[i][j];
				if (!x) {
					x = b[i][j];
					if (c[i + 1][j]) Init(x, c[i + 1][j]);
					if (c[i][j - 1]) Init(x, c[i][j - 1]);
					if (!c[i + 1][j] && !c[i][j - 1]) Init(x, max(b[i + 1][j - 1], c[i + 1][j - 1]));
				} else {
					if (b[i + 1][j]) Init(x, b[i + 1][j]);
					if (b[i][j - 1]) Init(x, b[i][j - 1]);
					if (b[i + 1][j - 1]) Init(x, b[i + 1][j - 1]);
				}
				if (c[i - 1][j] == S || c[i][j + 1] == S || c[i - 1][j + 1] == S) Init(S, x);
			}
		}
		f[S] = 1, que[hd = tl = 1] = S;
		while (hd <= tl) {
			int x = que[hd ++];
			RepE(i, x) {
				int y = g[i].y;
				(f[y] += f[x]) %= mod, in[y] --;
				if (!in[y]) que[++ tl] = y;
			}
		}
		return int(f[T] * num % mod);
	}
};


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值