此题P6008 [USACO20JAN] Cave Paintings P [计数][并查集]
传送门
我们发现,若从上往下遍历矩阵会涉及到一些类似删除的操作,这是难以维护的
所以可以从下往上,也就是从高度低往高度高遍历,这样变成添加操作维护起来是便捷的
根据乘法原理,最终答案为所有连通块方案数的乘积。考虑一个连通块的方案数怎么计算,若一个连通块只占据某一行,那么方案数为\(2\),即选或不选。
若连通块占据超过两行,就需要递推统计。
例如对于
.
. .
这样的连通块方案数为3
而对于
. . . . . . .
. . # # # . #
# # # # # . .
这样的连通块,首先不看第一行,计算后两行的两个连通块方案数分别为\(2\)和\(3\),则通过第一行将他们连在一起之后的方案数应该为\(7\),因为可以第一行不取,此时对于分开的两个小连通块应该是乘法原理,若第一行取,则整个连通块都会充满水
这可以用并查集维护
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6 + 10;
const int inf = 1e18 + 10;
const int mod = 1e9 + 7;
int n,m,fa[N],rk[N],s[N];
char mp[1010][1010];
int pos[1010][1010],cnt;
int find(int x) {
return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
void unionn(int x,int y) {
x = find(x);
y = find(y);
fa[x] = y;
}
int quickMul(int x,int k) {
int res = 1;
while(k) {
if(k & 1) res = res * x % mod;
x = x * x % mod;
k >>= 1;
}
return res;
}
void solve() {
cin >> n >> m;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++) {
cin >> mp[i][j];
if(mp[i][j] == '.') {
pos[i][j] = ++cnt;
fa[cnt] = cnt;
rk[cnt] = 1;
s[cnt] = 1;
}
}
int ans = 1,num = 0;
unordered_map<int,bool> vis;
for(int i = n;i >= 1;i--) {
vis.clear();
for(int j = 1;j <= m;j++) {
if(mp[i][j] != '.') continue;
if(mp[i][j - 1] == '.') unionn(pos[i][j],pos[i][j - 1]); //首先将同一行联通的加入并查集
}
for(int j = 1;j <= m;j++) {
if(mp[i][j] != '.') continue;
if(mp[i + 1][j] == '.' && find(pos[i][j]) != find(pos[i + 1][j])) { //然后看当前行与下一行联通
s[find(pos[i][j])] *= rk[find(pos[i + 1][j])] * s[find(pos[i + 1][j])] % mod; //s记录所有下一行连通块方案数乘积,由于在当前行当中,可能两个空白段是通过下一行联系起来的,所以是rk*s
s[find(pos[i][j])] %= mod;
unionn(pos[i + 1][j],pos[i][j]);
}
}
for(int j = 1;j <= m;j++)
if(mp[i][j] == '.' && !vis[find(pos[i][j])]) {
rk[find(pos[i][j])] += s[find(pos[i][j])]; //rk记录当前连通块的方案数
rk[find(pos[i][j])] %= mod;
s[find(pos[i][j])] = 1;
vis[find(pos[i][j])] = 1;
}
}
vis.clear();
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++) {
if(mp[i][j] == '.' && !vis[find(pos[i][j])])
ans = ans * rk[find(pos[i][j])] % mod;
vis[find(pos[i][j])] = 1;
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int t = 1;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号