loj#2542 [PKUWC2018]随机游走 (概率期望、组合数学、子集和变换、Min-Max容斥)
loj#2542 [PKUWC2018]隨機游走 (概率期望、組合數學、子集和變換、Min-Max容斥)
很好很有趣很神仙的題!
題目鏈接: https://loj.ac/problem/2542
題意: 請自行閱讀
題解首先我們顯然要求的是幾個隨機變量的最大值的期望(不是期望的最大值),然后這玩意很難求,根據Min-Max容斥化成最小值的期望來求。
Minn-max容斥是指\(\max(x_1,x_2,...,x_n)=\sum{S\in {1,2,...,n}} (-1)^{|S|-1} \min_{i\in S}(x_i)\) (所有元素都是正整數,這個盡管式子本身和期望沒關系但是經常是求期望的時候用它)
這個式子可以如此理解: 若把每個正整數\(x\)看作集合\({1,2,...,x}\)的話,則\(\max\)就是集合并,\(\min\)就是集合交,容斥原理直接推論
所以我們\(O(2^n)\)枚舉每個關鍵點的子集,然后問題轉化為: 按照同樣的規則隨機游走,走到任何一個關鍵點時即停,問期望步數
然后可以設\(dp[x]\)表示\(x\)節點作為起始點的期望步數
若\(x\)是關鍵點,\(dp[x]=0\), 否則\(dp[x]=\frac{1}{du[x]}\sum_{Edge(u,v)}{dp[v]}+1\) (\(du[]\)是度數)
然后我們就可以愉快地來個高斯消元\(O(2^nn^3)\)處理單個詢問了。(能得多少分別問我,沒試過……)
神仙之處在下面: \(O(n)\)求解樹上高消
由于這是棵樹,所以我們如果dfs的話,可以把\(dp[x]\)記成一個關于\(dp[fa]\) (\(fa\)是父親)的一次函數,形如\(dp[x]=A_xdp[fa]+B_x\).
然后假設現在做到點\(u\)(非根)則\[dp[u]=\frac{dp[fa]+\sum_{v\in son(u)}{A_vdp[u]+B_v}}{du[u]}+1\]
\[(1-\frac{\sum_{v\in son(u)}{a_v}}{du[u]})dp[u]=\frac{dp[fa]}{du[u]}+(\frac{\sum_{v\in son(u)}B_v}{du[u]}+1)\]
歸納易證,只要有特殊點(\(A=B=0\))的存在,等式左邊\(dp[u]\)的系數恒大于\(0\), 因此除過去就完成了\(A\)和\(B\)的遞推!
裸做時間復雜度\(O(2^nnq)\), 子集和變換(高維前綴和,又稱FMT)可以做到\(O(2^nn)\)預處理\(O(1)\)查詢
代碼
#include<cstdio> #include<cstdlib> #include<cstring> #include<utility> #define llong long long using namespace std;const int N = 18; const int P = 998244353; llong fact[(1<<N)+3],finv[(1<<N)+3]; struct Edge {int v,nxt; } e[(N<<1)+3]; int cnt[(1<<N)+3]; llong f[(1<<N)+3]; int fe[N+3]; int fa[N+3]; int du[N+3]; int n,q,s,en;llong quickpow(llong x,llong y) {llong cur = x,ret = 1ll;for(int i=0; y; i++){if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}cur = cur*cur%P;}return ret; } llong mulinv(llong x) {return quickpow(x,P-2);}void addedge(int u,int v) {du[u]++;en++; e[en].v = v;e[en].nxt = fe[u]; fe[u] = en; }void dfs0(int u) {for(int i=fe[u]; i; i=e[i].nxt){if(e[i].v==fa[u]) continue;fa[e[i].v] = u;dfs0(e[i].v);} }pair<llong,llong> dfs(int u,int sta) { // printf("dfs(%d)\n",u);if(sta&(1<<u)) {return make_pair(0,0);}pair<llong,llong> ret = make_pair(1,1);if(u!=s && du[u]==1) return ret;llong s1 = 0ll,s2 = 0ll;for(int i=fe[u]; i; i=e[i].nxt){if(e[i].v==fa[u]) continue;pair<llong,llong> tmp = dfs(e[i].v,sta);s1 = (s1+tmp.first)%P,s2 = (s2+tmp.second)%P;}ret.first = mulinv(du[u]-s1+P)%P; ret.second = ret.first*(s2+du[u])%P;return ret; }int main() {cnt[0] = 0; for(int i=1; i<(1<<N); i++) cnt[i] = cnt[i>>1]+(i&1);scanf("%d%d%d",&n,&q,&s); s--;for(int i=1; i<n; i++){int x,y; scanf("%d%d",&x,&y); x--; y--;addedge(x,y); addedge(y,x);}fa[s] = -1; dfs0(s);for(int i=1; i<(1<<n); i++){if(i&(1<<s)) {f[i] = 0ll; continue;}pair<llong,llong> tmp = dfs(s,i);f[i] = tmp.second;if((cnt[i]&1)==0) {f[i] = P-f[i];}}for(int i=0; i<n; i++){for(int j=0; j<(1<<n); j++){if(j&(1<<i)) {f[j] = (f[j]+f[j^(1<<i)])%P;}}}for(int i=1; i<=q; i++){int n0; scanf("%d",&n0);int u = 0; for(int j=1; j<=n0; j++) {int x; scanf("%d",&x); x--; u+=(1<<x);}printf("%lld\n",f[u]);}return 0; } 發表于 2019-05-25 20:51 suncongbo 閱讀(...) 評論(...) 編輯 收藏 刷新評論刷新頁面返回頂部 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的loj#2542 [PKUWC2018]随机游走 (概率期望、组合数学、子集和变换、Min-Max容斥)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LOJ #2542 [PKUWC2018
- 下一篇: UOJ #214 [UNR #1]合唱队