G.Christmas Color Grid 2(枚举,Tarjan)
题意:
本题与问题EEE类似。有一个HHH行和WWW列的网格,每个单元格都被涂成红色或绿色。用(i,j)(i,j)(i,j)表示从上到下第iii行、从左到右第jjj列的单元格。(i,j)(i,j)(i,j)单元格的颜色由字符Si,jS_{i,j}Si,j表示,其Si,j=S_{i,j}=Si,j=".“表示(i,j)(i,j)(i,j)单元格是红色,而Si,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′)被认为是相邻的。
选择一个绿色单元格并随机地将其重新涂成红色。计算重新涂色后网格中绿色连通分量数量的期望值,结果对998244353998244353998244353取模。
分析:
对于本题,枚举要修改的点,可以把相邻的绿色互相连边,这样操作之后原题就成了一个无向图上删掉一个点剩下的连通块个数。
可以使用TarjanTarjanTarjan算法,设uuu为当前结点,vvv为子节点,若跑完vvv后,得到dfnn≤lowvdfn_n \le low_vdfnn≤lowv,则说明从uuu的父亲结点,经过uuu才能够到达vvv,如果把结点uuu删掉,则不能到达结点vvv了。
这就相当于删去结点uuu时,uuu的父亲结点与结点vvv会被分成两个连通块,即这两部分无法相互到达,因此只需要数一下对于结点uuu,它有多少个vvv,满足dfnn≤lowvdfn_n \le low_vdfnn≤lowv,当然其实uuu的父亲结点也算是一个儿子节点,因此需要加一表示算上父亲节点。但每次TarjanTarjanTarjan的起点是没有父亲节点的,所以不需要加一。
综上,删去一个结点uuu会造成的影响为:假设删去结点u会增加aua_uau个连通块,这个无向图初始包含mmm个连通块,这个节点原来是属于1个连通块的,它删去后会增加aua_uau个连通块,那么删去这个结点后连通块的数量应为m+au+1m+a_u+1m+au+1。
代码:
#include<bits/stdc++.h>
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,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。

1268

被折叠的 条评论
为什么被折叠?



