题目描述
牛牛拿到了一个n*n的方阵,每个格子上面有一个数字:0或1
行和列的编号都是从0到n-1
现在牛牛每次操作可以点击一个写着1的格子,将这个格子所在的1连通块全部变成0。
牛牛想知道,自己有多少种不同的方案,可以把全部格子的1都变成0?
所谓连通块,是指方阵中的两个正方形共用一条边,即(x,y)和以下4个坐标的数是连通的:(x-1,y)、(x+1,y)、(x,y-1)、(x,y+1)
这个问题对于牛牛来说可能太简单了。于是他将这个问题变得更加复杂:
他会选择一个格子,将这个格子上的数字修改成1(如果本来就是1,那么不进行任何改变),再去考虑“点一成零”的方案数。
牛牛想知道,每次“将某个格子修改成1”之后,“把全部格子的1都变成0”的方案数量。
ps:请注意,每次“将某个格子修改成1”之后,状态会保留到接下来的询问。具体请参考样例描述。
由于方案数可能过大,请对109+710^{9}+7109+7取模
输入描述:
第一行输入一个 nnn(1≤n≤5001 \leq n \leq 5001≤n≤500)
随后 nnn 行每行有一个 长度为 nnn 的字符串,字符串只可能包含 ‘0’ 或 ‘1’ 字符 ,表示整个方阵
接下来输入一个数 kkk,表示询问的次数。(1≤k≤1051 \leq k \leq 10^{5}1≤k≤105)
随后 kkk 行每行有 2 个整数 xxx 和 yyy 表示将 xxx 行 yyy 列的数字变为 1 的一次修改操作
0≤x,y≤n−10 \leq x, y \leq n -10≤x,y≤n−1
输出描述:
针对每一次变更数字的操作,输出当前的方案数
示例1
输入
3
100
001
000
3
0 1
1 1
1 2
输出
4
4
4
说明
将第0行第1列的数变成1之后,方阵变成了这样:
110
001
000
一共有3个1。假设行号作为x轴坐标,列号作为y轴坐标,设坐标为(0,0)的是1号,坐标为(0,1)的是2号,坐标为(1,2)的是3号。
那么共有以下四种方案:
1->3
2->3
3->1
3->2
所以输出4。
将第1行第1列的数变成1之后,方阵变成了这样:
110
011
000
一共有4个1,显然它们是连通的,只要选择任意一个1,那么就全部变成0了,所以是4种方案。
将第1行第2列的数变成1,方阵不
会有任何改变:
110
011
000
所以方案数依然为4。
思路
显然如果没有改变的话答案应该是联通块个数n, 1 * 2 * 3 * (n - 1) * n (按点的顺序), 乘以每个联通块的个数。
为便于分析借用一张图片
细节见于代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 510;
int n, k;
char a[N][N];
int fa[N * N], siz[N * N];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1};
ll mod = 1e9 + 7;
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
int fx = find(x), fy = find(y);
if(fx != fy)
siz[fx] += siz[fy], fa[fy] = fx;
}
ll qsm(ll a, ll b)
{
ll ans = 1;
while(b > 0)
{
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans % mod;
}
ll ni(int x)
{
return qsm(x, mod - 2);
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf(" %s", a[i] + 1);//cout << "(((((";
n ++;
for(int i = 0; i <= n * n; i++) fa[i] = i, siz[i] = 1;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(a[i][j] == '1')
{
for(int k = 0; k < 4; k++)
{
int x1 = i + dx[k], y1 = j + dy[k];
if(a[x1][y1] == '1') merge(i * n + j, x1 * n + y1);
}
}
}
}
int cnt = 0;
ll ans = 1;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(fa[i * n + j] == i * n + j && a[i][j] == '1')
cnt ++, ans = (ans * siz[i * n + j]) % mod;
for(int i = 1; i <= cnt; i++)
ans = (ans * i) % mod;
scanf("%d", &k);
while(k --)
{
int x, y;
scanf("%d %d", &x, &y);
x ++, y ++;
if(a[x][y] == '1')
{
printf("%lld\n", ans);
continue;
}
a[x][y] = '1';
cnt ++;
ans = ans * cnt % mod;
for(int i = 0; i < 4; i++)
{
int x1 = dx[i] + x, y1 = dy[i] + y;
if(a[x1][y1] == '1')
{
int f1 = find(x * n + y);
int f2 = find(x1 * n + y1);
if(f1 != f2)
{
ans = ans * ni(cnt) % mod;
ans = ans * ni(siz[f1]) % mod;
ans = ans * ni(siz[f2]) % mod;
ans = ans * (siz[f1] + siz[f2]) % mod;
cnt --;
merge(f1, f2);
}
}
}
printf("%lld\n", ans);
}
return 0;
}