前置知识:简单并查集、简单逆元、简单组合数学
本题数据量很小于是可以暴力合并,我这里是用dfs的,这样并查集就不用重复路径压缩了。
用并查集维护,新加进来的数也可以实时合并或增加集合。
答案其实就是每个集合里面的点数累乘,最后乘一个集合数量的全排列即可。
详细来说就是:
- 把所有相邻的1合并到同一集合里
- 用并查集可以找到一共有多少个集合,以及这些集合里有多少个数
- 答案就是每个集合里面元素数量相乘,然后考虑集合之间也有顺序,所以乘一个集合数量的全排列
- 新加进来的1考虑它周围能否合并,以及能否合并多个集合,如果可以,就需要维护之前的累乘:除一下之前多乘的集合数量(集合数量少了),除之前分散的集合中元素数,然后再乘上新合并的集合元素数即可。
编程使用了一些技巧比如宏函数.
#include <bits/stdc++.h> #define sc(x) scanf("%d", &(x)) #define pr(x) printf("%lld\n", (x)) #define rep(i, l, r) for (int i = (l); i <= (r); ++i) #define pos(x, y) ((x - 1) * n + (y)) #define out(x, y) ((x) < 1 || (x) > n || (y) < 1 || (y) > n) using namespace std; typedef long long ll; const int N = 500 + 7; const int mod = 1e9 + 7; ll qkpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } ll getInv(ll a) { return qkpow(a, mod - 2); } //求一个数的逆元 int sz[N * N]; int fa[N * N]; inline void init(int n) { for (int i = 0; i <= n; ++i) fa[i] = i, sz[i] = 1; } inline int Find(int x) { if (x != fa[x]) fa[x] = Find(fa[x]); //路径压缩 return fa[x]; } void merge(int x, int y) { int fx = Find(x); int fy = Find(y); if (fx != fy) fa[fx] = fy, sz[fy] += sz[fx]; } int n; char g[N][N]; bool vis[N][N]; int dx[] = {1, -1, 0, 0}; int dy[] = {0, 0, 1, -1}; void dfs(int x, int y, int fa) { if (out(x, y)) return; vis[x][y] = 1; if (pos(x, y) != fa) merge(pos(x, y), fa); rep(i, 0, 3) { if (g[x + dx[i]][y + dy[i]] == '1' && !vis[x + dx[i]][y + dy[i]]) dfs(x + dx[i], y + dy[i], fa); } } ll ans = 1; ll cnt = 0; //集合数量 void chk(int x, int y) { if (g[x][y] == '1') return; g[x][y] = '1'; ++cnt; ans = ans * cnt % mod; int now = pos(x, y); rep(i, 0, 3) { if (!out(x + dx[i], y + dy[i]) && g[x + dx[i]][y + dy[i]] == '1') { int nxt = pos(x + dx[i], y + dy[i]); int f1 = Find(now); int f2 = Find(nxt); if (f1 != f2) { //维护 ans = ans * getInv(cnt) % mod; ans = ans * getInv(sz[f1]) % mod; ans = ans * getInv(sz[f2]) % mod; ans = ans * (sz[f1] + sz[f2]) % mod; --cnt; merge(f1, f2); } } } } int main() { sc(n); init(n * n); rep(i, 1, n) scanf(" %s", g[i] + 1); rep(i, 1, n) rep(j, 1, n) //合并 if (!vis[i][j] && g[i][j] == '1') dfs(i, j, pos(i, j)); rep(i, 1, n) rep(j, 1, n) { //集合内元素累乘 int now = pos(i, j); if (g[i][j] == '1' && Find(now) == now) ++cnt, ans = (ans * sz[now]) % mod; } rep(i, 1, cnt) ans = ans * i % mod; //全排列 int t, u, v; sc(t); while (t--) { sc(u), sc(v); chk(++u, ++v); pr(ans); } return 0; }