#2542. 「PKUWC 2018」隨機遊走(最值反演 + 樹上期望dp)
寫在這道題前面 : 網上的一些題解都不講那個系數是怎麽推得真的不良心 TAT
(不是每個人都有那麽厲害啊 , 我好菜啊)
而且 LOJ 過的代碼千篇一律 ... 那個系數根本看不出來是什麽啊 TAT
後來做了 HDU 4035 終於會了.... 感謝 雕哥的幫助 !!!
題意 : #2542. 「PKUWC 2018」隨機遊走
題解 :
原本的模型好像我不會那個暴力dp .... 就是直接統計點集中最後經過的點的期望 , 也就是點集中到所有點步數最大值的期望 . (也許可以列方程高斯消元 ? 似乎沒分)
但我們考慮轉化一下 (因為原來 有道CLJ的題 也是求這個) 把最大值的期望用 最值反演(MinMax容斥)
最值反演 (又稱 MinMax容斥 ) :
\[\displaystyle \max\{S\}=\sum_{T\subseteq S}(-1)^{|T|-1}\min\{T\}\]
其中 \(S\) 是全集 , \(T\) 是它的一個子集 , 就有這個神奇的定理 ...
證明 ( 來自 DOFY大大的博客 ) :
設最大值為 \(x \in S\) ,那麽構造映射 \(f(T) \to x \in T~?~T?x:T+x\) , 也就是有 \(x\) 就去掉 , 沒有就加上 。那麽當 \(T\) 不為空和 \({x}\) 時,\(T\) 與 \(f(T)\)
因為只相差一個最大值,最小值肯定相同,集合大小只相差 \(1\) ,就抵消了(一一映射),因為空集沒有最小值,所以最後只剩下 \(\{x\}\) 的貢獻。
然後有了這個 , 每次我們只需要求經過點集中點步數最少的貢獻 .
假設我們當前有一個集合 \(S\) , 我們用 \(f(u)\) 表示從 \(u\) 出發 , 第一次訪問 \(S\) 中節點的期望步數 .
所以我們有一些顯然的式子 :
\(u \in S:\)
\[f(u)=0\]
\(u \not \in S:\)
令 \(d[u]\) 為 \(u\) 在樹上的度數(連出來邊數) , \(\mathrm{ch}[u]\) 為 \(u\)
\[\displaystyle f(u)=[f(\mathrm{fa}[u])+1+\sum (f(\mathrm{ch[u]})+1)] \times \frac{1}{d[u]}\]
\[\displaystyle =\frac{1}{d[u]}f(\mathrm{fa}[u])+\frac{1}{d[u]}\sum f(\mathrm{ch}[u])+1\]
不難發現 每個點的答案可以只保留它父親的答案和一個常數的貢獻
( 可以理解成全都能倒推回去 , 因為那個就算沒有 \(u \in S\) 的限制 , 葉子的貢獻也只與父親有關 )
假設令它為 \[f(u)=A_uf(\mathrm{fa}[u])+B_u\] 以及 \(v = \mathrm{ch}[u]\)
那麽有 \[\displaystyle \sum f(\mathrm{ch[u]})=\sum f(v) = \sum(A_v f_u = B_v)\]
把這個回代就有
\[\displaystyle (1-\frac{\sum A_v}{d[u]}) f(u) = \frac{1}{d[u]}f(\mathrm{fa}[u])+(1+\frac{B_v}{d[u]})\]
除過去就可以得到每個遞推式的 \(A,B\) 了 qwq
然後隨便寫寫就行啦 , 復雜度 \(O((n+Q) \cdot 2^n)\) ... 其實後面那個復雜度是對於每個詢問枚舉子集 .
預處理的話 , 復雜度就變成 \(O(n\cdot 2^n + 3^n)\) 啦 ...
似乎都可以輕松過掉 ?
- 代碼 :
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == ‘-‘) fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return x * fh;
}
void File() {
#ifdef zjp_shadow
freopen ("2542.in", "r", stdin);
freopen ("2542.out", "w", stdout);
#endif
}
typedef long long ll;
const int Mod = 998244353;
inline ll fpm(ll x, int power) {
ll res = 1; x = (x % Mod + Mod) % Mod;
for (; power; power >>= 1, (x *= x) %= Mod)
if (power & 1) (res *= x) %= Mod;
return res;
}
const int N = 20;
int n, Q, rt, d[N];
vector<int> G[N]; ll A[N], B[N], invd[N];
void Dp(int u, int fa, int S) {
if ((1 << (u - 1)) & S) { A[u] = B[u] = 0; return ; }
ll totA = 0, totB = 0;
for (int v : G[u]) if (v ^ fa)
Dp(v, u, S), totA += A[v], totB += B[v];
totA %= Mod, totB %= Mod;
ll coef = fpm(Mod + 1 - totA * invd[u], Mod - 2);
A[u] = invd[u] * coef % Mod;
B[u] = (1 + totB * invd[u] % Mod) * coef % Mod;
}
ll Minv[1 << 18]; int bit[1 << 18];
int main () {
File();
n = read(); Q = read(); rt = read();
For (i, 1, n - 1) {
int u = read(), v = read();
G[u].push_back(v); G[v].push_back(u);
++ d[u]; ++ d[v];
}
For (i, 1, n) invd[i] = fpm(d[i], Mod - 2);
int maxsta = (1 << n) - 1;
For (i, 0, maxsta) {
Dp(rt, 0, i);
Minv[i] = B[rt];
bit[i] = bit[i >> 1] + (i & 1);
}
while (Q --) {
int k = read(), sta = 0;
while (k --) sta |= (1 << (read() - 1));
ll ans = 0;
for (int i = sta; i; i = (i - 1) & sta)
ans += (bit[i] & 1 ? 1 : -1) * Minv[i];
ans = (ans % Mod + Mod) % Mod;
printf ("%lld\n", ans);
}
return 0;
}
#2542. 「PKUWC 2018」隨機遊走(最值反演 + 樹上期望dp)