[USACO24JAN] Island Vacation P 题解
题解
建出原图的广义圆方树,令所有方点的编号为
原图上的随机游走等价于,设当前在圆方树上的点
-
如果
u 是圆点:首先有p_u 的概率停下。设
s=[\mathrm{val}_{\mathrm{fa}_u}>1]+\sum_{v\in \mathrm{son}_u} \mathrm{val}_v ,那么有[\mathrm{val}_{\mathrm{fa}_u}>1]\cdot s^{-1} 的概率走向u 的下一个兄弟。这里“下一个兄弟”取决于从u 的父亲走下来时的方向。此外,如果u 是最后一个点,那么回到u 的父亲。对于
u 的每个没有被访问过的儿子v ,有\mathrm{val}_v\cdot s^{-1} 的概率走向v 。 - 如果
u 是方点:如果以前没有访问过u ,那么等概率走向u 的第一个儿子或最后一个儿子,否则回到u 的父亲。
也就是说,对于一个圆点,我们可能走到它的一些儿子,然后绕一圈回来,最后可能走到它的兄弟,或是停在它上面。
开始 DP:设
对于每个点
算出
- 如果
u 是圆点,那么f_{u,0} 是从\mathrm{fa}_u 走到\mathrm{fa}_u 的第一个儿子再走到u 的概率,f_{u,1} 同理; - 如果
u 是方点,那么f_{u,0} 表示走到u 的概率,而f_{u,1}=0 。
由于已经算出了
具体地,从
那么
(
而停在
时间复杂度
代码
#include <bits/stdc++.h>
using namespace std;
#define For(Ti, Ta, Tb) for (auto Ti = (Ta); Ti <= (Tb); ++Ti)
#define Dec(Ti, Ta, Tb) for (auto Ti = (Ta); Ti >= (Tb); --Ti)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define range(Tx) begin(Tx), end(Tx)
using ll = long long;
using mint = modint1000000007;
const int N = 1e4 + 5;
int T, n, m, dfn[N], low[N], dfx, stk[N], top, tot;
mint p[N];
vector<int> gr[N], e[N * 2];
void link(int u, int v) {
e[u].push_back(v);
e[v].push_back(u);
}
void tarjan(int u) {
low[u] = dfn[u] = ++dfx;
stk[++top] = u;
for (int v : gr[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
if (low[v] == dfn[u]) {
link(++tot, u);
for (int x = 0; x != v;) {
link(tot, x = stk[top--]);
}
}
} else {
low[u] = min(low[u], dfn[v]);
}
}
}
int fa[N * 2], val[N * 2], pre[N], nxt[N], sum[N];
mint f[N * 2][2], g[N], h[N * 2], ans[N];
void get_dp(int u, int &tot, mint *dp) {
fill(dp, dp + n + 1, 0);
dp[0] = 1;
for (int v : e[u]) {
if (v != fa[u] && val[v] > 1) {
Dec(i, tot, 0) { dp[i + 1] += dp[i] * h[v]; }
++tot;
}
}
}
void dfs(int u) {
val[u] = (u <= n ? val[fa[u]] : min(2, int(e[u].size()) - 1));
sum[u] = (val[u] > 1);
for (int v : e[u]) {
if (v != fa[u]) {
fa[v] = u;
dfs(v);
sum[u] += val[v];
}
}
if (u > n) {
int x = u;
for (int v : e[u]) {
if (v != fa[u]) {
pre[v] = x;
x = v;
}
}
reverse(range(e[u]));
x = u;
for (int v : e[u]) {
if (v != fa[u]) {
nxt[v] = x;
x = v;
}
}
reverse(range(e[u]));
}
if (val[u] <= 1)
return;
if (u <= n) {
static mint dp[N];
int tot = 0;
get_dp(u, tot, dp);
mint pw = 1;
For(i, 0, tot) {
pw *= 1 - p[u];
g[u] += dp[i] * C.fac(i) * pw * C.inv(sum[u] - i * 2);
pw *= 2 * C.inv(sum[u] - i * 2);
}
} else {
h[u] = 1;
for (int v : e[u]) {
if (v != fa[u]) {
h[u] *= g[v];
}
}
}
}
void dfs2(int u) {
if (u <= n) {
static mint dp[N];
int tot = 0;
get_dp(u, tot, dp);
mint all = 1 - g[u];
for (int v : e[u]) {
if (v == fa[u])
continue;
if (val[v] > 1) {
For(i, 1, tot) { dp[i] -= dp[i - 1] * h[v]; }
--tot;
}
mint pr = 0, pw = 1;
For(i, 0, tot) {
pw *= 1 - p[u];
pr += dp[i] * C.fac(i) * pw * val[v] * C.inv(sum[u] - i * 2);
pw *= 2 * C.inv(sum[u] - i * 2);
}
all -= pr * (1 - h[v]);
f[v][0] += pr * (f[u][0] + f[u][1]);
if (val[v] > 1) {
++tot;
Dec(i, tot, 1) { dp[i] += dp[i - 1] * h[v]; }
}
}
ans[u] = (f[u][0] + f[u][1]) * all;
} else {
mint cur = f[u][0] / 2;
for (int v : e[u]) {
if (v != fa[u]) {
f[v][0] = cur;
cur *= g[v];
}
}
reverse(range(e[u]));
cur = f[u][0] / 2;
for (int v : e[u]) {
if (v != fa[u]) {
f[v][1] = cur;
cur *= g[v];
}
}
reverse(range(e[u]));
}
for (int v : e[u]) {
if (v != fa[u]) {
dfs2(v);
}
}
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> T;
while (T--) {
cin >> n >> m;
For(i, 1, n) { cin >> p[i]; }
For(i, 1, m) {
int u, v;
cin >> u >> v;
gr[u].push_back(v);
gr[v].push_back(u);
}
tot = n;
tarjan(1);
f[1][0] = 1;
dfs(1);
dfs2(1);
For(i, 1, n) { cout << ans[i] << " \n"[i == n]; }
For(i, 1, n) {
dfn[i] = low[i] = 0;
g[i] = ans[i] = 0;
gr[i].clear();
}
For(i, 1, tot) {
f[i][0] = f[i][1] = h[i] = fa[i] = 0;
e[i].clear();
}
dfx = top = 0;
}
return 0;
}
有板子的:https://loj.ac/s/2014541。