本题与问题 E E E类似。有一个 H H H行和 W W W列的网格,每个单元格都被涂成红色或绿色。用 ( i , j ) (i,j) (i,j)表示从上到下第 i i i行、从左到右第 j j j列的单元格。 ( i , j ) (i,j) (i,j)单元格的颜色由字符 S i , j S_{i,j} Si,j表示,其 S i , j = S_{i,j}= Si,j=".“表示 ( i , j ) (i,j) (i,j)单元格是红色,而 S i , j = S_{i,j}= Si,j=”#"表示 ( i , j ) (i,j) (i,j)单元格是绿色。
定义绿色连通分量是指顶点集是绿色单元格、边缘集为连接两个相邻绿色单元格的单元格的集合。当 ∣ x − x ′ ∣ + ∣ y − y ′ ∣ = 1 \lvert x−x^′\rvert + \lvert y−y^′\rvert=1 ∣x−x′∣+∣y−y′∣=1时,单元格 ( x , y ) (x,y) (x,y)和 ( x ′ , y ′ ) (x',y') (x′,y′)被认为是相邻的。
选择一个绿色单元格并随机地将其重新涂成红色。计算重新涂色后网格中绿色连通分量数量的期望值,结果对 998244353 998244353 998244353取模。
对于本题,枚举要修改的点,可以把相邻的绿色互相连边,这样操作之后原题就成了一个无向图上删掉一个点剩下的连通块个数。
可以使用 T a r j a n Tarjan Tarjan算法,设 u u u为当前结点, v v v为子节点,若跑完 v v v后,得到 d f n n ≤ l o w v dfn_n \le low_v dfnn≤lowv,则说明从 u u u的父亲结点,经过 u u u才能够到达 v v v,如果把结点 u u u删掉,则不能到达结点 v v v了。
这就相当于删去结点 u u u时, u u u的父亲结点与结点 v v v会被分成两个连通块,即这两部分无法相互到达,因此只需要数一下对于结点 u u u,它有多少个 v v v,满足 d f n n ≤ l o w v dfn_n \le low_v dfnn≤lowv,当然其实 u u u的父亲结点也算是一个儿子节点,因此需要加一表示算上父亲节点。但每次 T a r j a n Tarjan Tarjan的起点是没有父亲节点的,所以不需要加一。
综上,删去一个结点 u u u会造成的影响为:假设删去结点u会增加 a u a_u au个连通块,这个无向图初始包含 m m m个连通块,这个节点原来是属于1个连通块的,它删去后会增加 a u a_u au个连通块,那么删去这个结点后连通块的数量应为 m + a u + 1 m+a_u+1 m+au+1。
#include
using namespace std;
typedef long long LL;
const LL MOD = 998244353;
char mp[1005][1005];
LL n, m;
LL head[1000005], nxt[10000005], to[10000005], ecnt;
void add(LL u, LL v) {
to[++ecnt] = v;
nxt[ecnt] = head[u];
head[u] = ecnt;
}
void adde(LL x, LL y) {
add(x, y);
add(y, x);
}
LL f(LL x, LL y) {
return (x - 1) * m + y;
}
LL dfn[1000005], low[1000005], deg[2000005];
LL ncnt;
LL stk[1000005], sz;
void tarjan(LL x) {
stk[++sz] = x;
dfn[x] = low[x] = ++ncnt;
for (LL i = head[x]; i; i = nxt[i]) {
LL v = to[i];
if (!dfn[v]) {
tarjan(v);
low[x] = min(low[x], low[v]);
if (low[v] == dfn[x]) {
LL t;
do {
t = stk[sz--];
deg[t]++;
} while (t != x);
stk[++sz] = x;
}
} else
low[x] = min(low[x], dfn[v]);
}
}
LL qpow(LL x, LL y) {
LL ret = 1;
while (y) {
if (y & 1)
ret = ret * x % MOD;
x = x * x % MOD;
y >>= 1;
}
return ret;
}
int main() {
for (LL i = 1; i <= n; i++) {
string str;
cin >> str;
for (LL j = 1; j <= m; j++) {
mp[i][j] = str[j - 1];
if (mp[i][j] == '#') {
if (mp[i - 1][j] == mp[i][j])
adde(f(i - 1, j), f(i, j));
if (mp[i][j - 1] == mp[i][j])
adde(f(i, j - 1), f(i, j));
}
}
}
LL xcnt = 0, cnt = 0, tot = 0;
for (LL i = 1; i <= n; i++) {
for (LL j = 1; j <= m; j++) {
cnt += (mp[i][j] == '#');
if (mp[i][j] == '#' && !dfn[f(i, j)])
tarjan(f(i, j)), ++xcnt;
}
}
for (LL i = 1; i <= n; i++) {
for (LL j = 1; j <= m; j++) {
if (mp[i][j] == '#') {
tot += xcnt - 1 + deg[f(i, j)];
tot >= MOD ? (tot -= MOD) : 0;
}
}
}
cout << tot * qpow(cnt, MOD - 2) % MOD;
return 0;
}
以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。