[PKUWC2018]随机游走(Min-max容斥+FWT/FMT)
题目:洛谷P5643、LOJ#2542
题目描述:
一棵\(n\)个点的树,从\(x\)出发,每次等概率选择一条与之相连的边,朝那条边连着的另外一个节点走过去
\(Q\)次询问,每次询问这\(n\)个点的一个子集\(S\),求要把\(S\)中的所有点经过至少一次的最小期望步数
\(x\)一开始就经过了一次
\(n \leq 18\),\(Q \leq 5000\)
蒟蒻题解:
对于确定了一个集合\(S\),令\(max(S)\)表示经过\(S\)所有的点至少一次,\(min(T)\)表示经过\(T\)中任意一个点一次,根据\(Min-max\)容斥,则有:
其中\(E(max(S))\)为经过\(S\)所有点至少一次的最小期望步数,\(E(min(T))\)为经过\(T\)中任意一个点一次的最小期望步数
考虑计算\(E(min(T))\),令\(f(i)\)为从\(i\)出发知道到达\(T\)中任意一个点为止的期望步数
- 若\(i \in T\),则\(f(i) = 0\)
- 若\(i \notin T\),则\(f(i) = 1 + \frac{1}{deg(i)} \sum_{(i,j) \in E}f(j)\)
现在我们想要知道的是\(f(x)\)的答案,不妨让\(x\)作为这棵树的根
由于最终是要算出根节点的答案,尝试将\(fa_i\)单独提出来
由于叶子节点和\(S\)集合中的点是不需要考虑孩子的情况的,我们可以尝试将每个\(f(i)\)表示成\(A_i + B_if(fa_i)\)
这样我们就可以得到:
其中\(A(i) = \frac{deg(i) + \sum_{i = fa_j}A_j}{deg(i) - \sum_{i = fa_j}B_j}\),\(B(i) = \frac{1}{deg(i) - \sum_{i = fa_j}B_j}\)
可以一直往上转移,由于\(x\)为根,它没有父亲,所以\(f(x) = A(x)\)
这样我们就可以\(\mathcal O(n)\)算出经过集合\(T\)任意一个点为止的期望步数\(E(min(T))\)
预处理出所以的\(E(min(T))\),就可以在\(\mathcal O(q2^n)\)时间内算出答案
观察\(Min-max\)容斥的形式:
我们可以令\(g(T)\)表示\((-1)^{|T|-1} E(min(T))\),则有:
对于求所有的子集之和,可以用\(FWT/FMT\)优化到\(\mathcal O(n2^n)\),这边用的是\(FWT\)或变换
参考程序:
#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;
const int N = 270000, p = 998244353;
int n, q, x, m, cnt, hea[20], nxt[40], to[40], A[20], B[20], ct[N], f[N];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline void write(int x)
{
int num = 0;
char sc[15];
if (!x) sc[num = 1] = 48;
while (x) sc[++num] = x % 10 + 48, x /= 10;
while (num) putchar(sc[num--]);
putchar('\n');
}
inline int fpow(ll x, int y)
{
ll z = 1;
while (y)
{
if (y & 1) z = z * x % p;
x = x * x % p, y >>= 1;
}
return z;
}
inline int inc(int x, int y)
{
x += y;
return x < p ? x : x - p;
}
inline void add(int x, int y)
{
nxt[++cnt] = hea[x], to[cnt] = y, hea[x] = cnt;
}
inline void dfs(int x, int fa, int S)
{
if ((S >> (x - 1)) & 1)
{
A[x] = B[x] = 0;
return;
}
int dg = fa ? 1 : 0, sA = 0, sB = 0;
for (Re i = hea[x]; i; i = nxt[i])
{
int u = to[i];
if (u == fa) continue;
dfs(u, x, S);
++dg, sA = inc(sA, A[u]), sB = inc(sB, B[u]);
}
B[x] = fpow(dg - sB + p, p - 2), A[x] = 1ll * (dg + sA) * B[x] % p;
}
int main()
{
n = read(), q = read(), x = read(), m = 1 << n;
for (Re i = 1; i < n; ++i)
{
int u = read(), v = read();
add(u, v), add(v, u);
}
for (Re i = 0; i < m; ++i) dfs(x, 0, i), ct[i] = ct[i >> 1] + (i & 1), f[i] = 1ll * ((ct[i] & 1) ? 1 : p - 1) * A[x] % p;
for (Re i = 1; i <= n; ++i)
{
int u = 1 << i, v = 1 << (i - 1);
for (Re j = 0; j < m; j += u)
for (Re k = 0; k < v; ++k) f[j | k | v] = inc(f[j | k | v], f[j | k]);
}
while (q--)
{
int u = read(), S = 0;
while (u--) S |= 1 << (read() - 1);
write(f[S]);
}
return 0;
}

浙公网安备 33010602011771号