专栏文章
题解:P6008 [USACO20JAN] Cave Paintings P
P6008题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mio9gaps
- 此快照首次捕获于
- 2025/12/02 15:32 3 个月前
- 此快照最后确认于
- 2025/12/02 15:32 3 个月前
P6008 题解
题目大意:
给定一张 的网格图,每个点为石头或空白。操作:将一个空白点改为水,如果最终不产生流动则此方案为一个合法的方案。求满足物理原理的空白部分填充方案数。
题目中的“物理原理”指重力作用以及连通器原理。(重力作用应该没人不会吧)
容易发现上层方块一定支配下层方块,考虑DP。
对于每个块,从下往上,依次处理每一行。
具体地,使用并查集,将当前位置 , 与其左右下的位置相连,合并时用 数组记录当前位置的方案数,即两边相乘。合并完后发现在最上面一行的无论哪个位置放水都会流动到下方任意一个联通的格子,所以合并后方案数应加 。
目标答案为:,即所有连通块答案的积。
代码
CPP#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m;
const int N = 1010;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
int fa[M], dp[M];
bool a[N][N], vis[M];
char ch[N][N];
int dx[5] = {0, -1, 0};
int dy[5] = {1, 0, -1};
inline int Hash(int x, int y){
return (x - 1) * m + y;
}
inline int find(int x){
if (fa[x] == x) return x;
else return fa[x] = find(fa[x]);
}
inline void merge(int x, int y){
int xx = find(x);
int yy = find(y);
if (xx != yy){
fa[yy] = xx;
dp[xx] = (dp[xx] * dp[yy]) % mod;
}
}
signed main(){
cin >> n >> m;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
int k = Hash(i, j);
fa[k] = k;
dp[k] = 1;
}
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
cin >> ch[i][j];
if (ch[i][j] == '#') a[i][j] = 1;
else a[i][j] = 0;
}
}
for (int i = n - 1; i > 1; i--){
for (int j = 2; j < m; j++){
for (int k = 0; k < 3; k++){
int xx = i + dx[k];
int yy = j + dy[k];
if (a[i][j] || a[xx][yy]) continue;
merge(Hash(i, j), Hash(xx, yy));
}
}
for (int j = 2; j < m; j++){
int k = find(Hash(i, j));
if (vis[k] || a[i][j]) continue;
vis[k] = 1;
dp[k] = (dp[k] + 1) % mod;
}
for (int j = 2; j < m; j++){
if (a[i][j]) continue;
int k = find(Hash(i, j));
vis[k] = 0;
}
}
int ans = 1;
for (int i = 2; i < n; i++){
for (int j = 2; j < m; j++){
int k = Hash(i, j);
if (find(k) == k && !a[i][j]){
ans = (ans * dp[k]) % mod;
}
}
}
cout << ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...