ARC196B - Torus Loop
妙妙题,考虑一个 \(A\) 瓷砖链接两个相邻的网格正方形棱,\(B\) 链接对边,考虑给每个正方形的边拆点,容易得出 \(A\) 瓷砖中,任意边选择后对边不选;\(B\) 瓷砖中,任意边选择后,对边都选,非对边都不选。最后缩点后的联通块数除以二就是合法的子块,对答案的贡献是 \(2\) 的幂次。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int N = 4e6 + 10, mod = 998244353;
int n, m, p[N];
ll qpow(ll a, ll k = mod - 2) {
ll res = 1;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
int up(int x, int y) {
x %= n; y %= m;
return (x * 2 * m + y) << 1;
}
int left(int x, int y) {
x %= n; y %= m;
return ((x * 2 + 1) * m + y) << 1;
}
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void add(int x, int y) {
p[find(x)] = find(y);
}
void addA(int x, int y) {
add(up(x, y), up(x + 1, y) ^ 1);
add(up(x, y) ^ 1, up(x + 1, y));
add(left(x, y), left(x, y + 1) ^ 1);
add(left(x, y) ^ 1, left(x, y + 1));
}
void addB(int x, int y) {
add(up(x, y), up(x + 1, y));
add(up(x, y) ^ 1, up(x + 1, y) ^ 1);
add(left(x, y), left(x, y + 1));
add(left(x, y) ^ 1, left(x, y + 1) ^ 1);
add(up(x, y), left(x, y) ^ 1);
add(up(x, y) ^ 1, left(x, y));
add(up(x + 1, y), left(x, y) ^ 1);
add(up(x + 1, y) ^ 1, left(x, y));
add(up(x, y), left(x, y + 1) ^ 1);
add(up(x, y) ^ 1, left(x, y + 1));
add(up(x + 1, y), left(x, y + 1) ^ 1);
add(up(x + 1, y) ^ 1, left(x, y + 1));
}
void solve() {
cin >> n >> m;
for (int i = 0; i < n * m << 2; i ++ ) p[i] = i;
vector<vector<char>> mp(n, vector<char>(m));
for (int i = 0; i < n; i ++ ) {
for (int j = 0; j < m; j ++ ) {
cin >> mp[i][j];
if (mp[i][j] == 'A') addA(i, j);
else addB(i, j);
}
}
int ans = 0;
unordered_map<int, int> st;
for (int i = 0; i < n * m << 2; i ++ ) {
if (find(i) == find(i ^ 1)) {
return cout << "0\n", void();
}
if (!st.count(find(i))) ans ++ ;
st[find(i)] = 1;
}
cout << qpow(2, ans >> 1) << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T -- ) solve();
return 0;
}