Description
给定一张 n n n 个点, m m m 条边的无向图。
有 q q q 次询问,每次询问给定一个集合 S S S 和一个点 x x x。求如果从 x x x 出发一直随机游走(每次等概率走向相邻的点),直到点集 S S S 中所有点都至少经过一次的话,期望经过多少条边。
n ≤ 18 , m ≤ n ( n − 1 ) 2 , q ≤ 100000 n≤18,m≤\frac{n(n-1)}{2},q≤100000 n≤18,m≤2n(n−1),q≤100000, 答案对 998244353 998244353 998244353 取模。
Solution
设 f [ S ] [ i ] f[S][i] f[S][i] 表示已经经过 S S S 集合中的所有点,现在位于点 i i i,将剩下的点全部经过,期望还要走多少条边(保证 i ∈ S i∈S i∈S)。
对于询问 S , x S,x S,x,答案就是 f [ ( a l l − S ) ∣ x ] [ x ] f[(all-S)|x][x] f[(all−S)∣x][x]。
初值 f [ a l l ] [ i ] = 0 f[all][i]=0 f[all][i]=0。
递推式(
d
u
d_u
du 表示点
u
u
u 的度数):
f
[
S
]
[
u
]
=
1
d
u
∑
u
,
v
有
边
(
f
[
S
∣
v
]
[
v
]
+
1
)
f[S][u]=\frac{1}{d_u}\sum_{u,v有边}(f[S|v][v]+1)
f[S][u]=du1u,v有边∑(f[S∣v][v]+1)
直接高斯消元是 O ( ( 2 n n ) 3 ) O((2^nn)^3) O((2nn)3) 的,过不去。
发现要么 S ∈ S ∣ v S∈S|v S∈S∣v,要么 S = S ∣ v S=S|v S=S∣v。
我们可以把方程改写成:
f
[
S
]
[
u
]
−
1
d
u
∑
u
,
v
有
边
,
S
∣
v
=
S
(
f
[
S
]
[
v
]
+
1
)
=
1
d
u
∑
u
,
v
有
边
,
S
∈
S
∣
v
(
f
[
S
∣
v
]
[
v
]
+
1
)
f[S][u]-\frac{1}{d_u}\sum_{u,v有边,S|v=S}(f[S][v]+1)=\frac{1}{d_u}\sum_{u,v有边,S∈S|v}(f[S|v][v]+1)
f[S][u]−du1u,v有边,S∣v=S∑(f[S][v]+1)=du1u,v有边,S∈S∣v∑(f[S∣v][v]+1)
考虑按 S S S 从大到小递推。
也就是说我们求 f [ S ] [ u ] f[S][u] f[S][u] 的时候已经知道所有的 f [ T ] [ v ] f[T][v] f[T][v] 了。( u , v u,v u,v 可以是任何一个点, S S S 是 T T T 的真子集。)
也就是说式子中 1 d u ∑ u , v 有 边 , S ∈ S ∣ v ( f [ S ∣ v ] [ v ] + 1 ) \frac{1}{d_u}\sum_{u,v有边,S∈S|v}(f[S|v][v]+1) du1∑u,v有边,S∈S∣v(f[S∣v][v]+1) 的值已知。
那么我们只要把 f [ S ] [ 1 ] f[S][1] f[S][1] ~ f [ S ] [ n ] f[S][n] f[S][n] 这 n n n 个变量拿出来跑高斯消元即可。
总共跑 2 n 2^n 2n 次高斯消元,时间复杂度 O ( 2 n n 3 ) O(2^nn^3) O(2nn3)。
Code
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353, e = 805, o = (1 << 18) + 5;
int adj[e], nxt[e], num, go[e], f[o][20], n, m, day, a[25][25], b[25], pos[25];
int c[25], tn, id[25], d[25], h[25], cnt;
inline void add(int x, int y)
{
nxt[++num] = adj[x];
adj[x] = num;
go[num] = y;
nxt[++num] = adj[y];
adj[y] = num;
go[num] = x;
}
inline int read()
{
char ch; int res;
while (ch = getchar(), ch < '0' || ch > '9');
res = ch - 48;
while (ch = getchar(), ch >= '0' && ch <= '9')
res = res * 10 + ch - 48;
return res;
}
inline int ksm(int x, int y)
{
int res = 1;
while (y)
{
if (y & 1) res = 1ll * res * x % mod;
y >>= 1;
x = 1ll * x * x % mod;
}
return res;
}
inline void put(int n)
{
int i, j;
for (i = 1; i <= n; i++, putchar('\n'))
{
for (j = 1; j <= n; j++) printf("%d ", a[i][j]);
printf(" %d", b[i]);
}
}
inline void gauss(int n)
{
int i, j, k;
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
a[i][j] = (a[i][j] % mod + mod) % mod;
for (i = 1; i <= n; i++) b[i] = (b[i] % mod + mod) % mod;
for (i = 1; i < n; i++)
{
k = i;
for (j = i; j <= n; j++)
if (a[j][i])
{
k = i;
break;
}
if (k != i)
for (j = i; j <= n; j++) swap(a[k][j], a[i][j]);
swap(b[k], b[i]);
for (j = i + 1; j <= n; j++)
if (a[j][i])
{
int x = 1ll * a[j][i] * ksm(a[i][i], mod - 2) % mod;
for (k = i; k <= n; k++)
a[j][k] = (a[j][k] - 1ll * a[i][k] * x % mod + mod) % mod;
b[j] = (b[j] - 1ll * b[i] * x % mod + mod) % mod;
}
}
for (i = n; i >= 1; i--)
{
c[i] = b[i];
for (j = i + 1; j <= n; j++)
c[i] = (c[i] - 1ll * a[i][j] * c[j] % mod + mod) % mod;
c[i] = 1ll * c[i] * ksm(a[i][i], mod - 2) % mod;
}
}
int main()
{
int i, j, x, y, s, u;
n = read(); m = read();
while (m--)
{
x = read();
y = read();
d[x]++;
d[y]++;
add(x, y);
}
tn = 1 << n;
for (s = tn - 2; s >= 0; s--)
{
cnt = 0;
for (i = 1; i <= n; i++)
if (s & (1 << i - 1))
{
id[++cnt] = i;
pos[i] = cnt;
}
memset(a, 0, sizeof(a));
for (i = 1; i <= cnt; i++)
{
int u = id[i];
a[i][i] = d[u];
b[i] = d[u];
for (j = adj[u]; j; j = nxt[j])
{
int v = go[j];
if (s & (1 << v - 1)) a[i][pos[v]] = mod - 1;
else b[i] = (b[i] + f[s | (1 << v - 1)][v]) % mod;
}
}
gauss(cnt);
for (i = 1; i <= cnt; i++) f[s][id[i]] = c[i];
}
day = read();
while (day--)
{
n = read();
y = tn - 1;
for (i = 1; i <= n; i++)
{
x = read();
y ^= 1 << x - 1;
}
u = read();
printf("%d\n", f[y | (1 << u - 1)][u]);
}
return 0;
}