1. 程式人生 > >LOJ2542 隨機遊走 Min-Max容斥+樹上期望DP

LOJ2542 隨機遊走 Min-Max容斥+樹上期望DP

while har struct 下午 所有 include php ack 表示

搞了一下午 真的是啥都不會
首先這道題要用到Min-Max容斥 得到的結論是

設 $Max(S)$表示集合裏最晚被訪問的節點被訪問的期望步數
設 $Min(S)$表示集合裏最早被訪問的節點被訪問的期望步數
那麽$ Max(S) = ∑_{T \in S} {-1^ { \lvert T \rvert+1} }Min(T)$
(這個相關的證明和理解可以看看HDU4336 附一個題解)

考慮對於一個集合$S$如何計算$Min(S)$
記$d_u$為點$u$的度數
當$u\notin S \space\space \Rightarrow \space \space\displaystyle f_u=f_{fa[u]}+1+\sum (f_{son[u]}+1)\times \frac{1}{d_u}$

當$u \in S\space\space \Rightarrow \space \space f(u)=0$
對於樹上的期望可以寫成$f_u=k_u\times f_{fa[u]}+b_u $的形式
於是$\sum f_{son[u]}=\sum_{fa[v]=u}(a_v \times 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})$
這個$dfs$一遍就可以維護所有點的$a,b$了

考慮如何回答詢問

可以對於每個詢問的集合$S$暴力枚舉子集 這樣是可以過得
但我們也可以向類似$FMT$的做法先維護出所有集合的子集之和再$O(1)$回答每個詢問 這裏註意每個集合初值的正負

#include<bits/stdc++.h>
using namespace std;
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define pa pair<int,int>
#define mod 998244353
#define ll long long #define mk make_pair #define pb push_back #define lb double #define fi first #define se second #define cl(x) memset(x,0,sizeof x) #ifdef Devil_Gary #define bug(x) cout<<(#x)<<" "<<(x)<<endl #define debug(...) fprintf(stderr, __VA_ARGS__) #else #define bug(x) #define debug(...) #endif const int INF = 0x7fffffff; const int N=1e6+5; const int M=25; /* char *TT,*mo,but[(1<<15)+2]; #define getchar() ((TT==mo&&(mo=(TT=but)+fread(but,1,1<<15,stdin),TT==mo))?-1:*TT++)//*/ inline int read(){ int x=0,rev=0,ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')rev=1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return rev?-x:x; } struct Edge{ int v,nxt; }e[N<<1]; int n,Q,rt,tot,head[M],bin[N],a[M],b[M],bit[N],d[M],f[N]; void add(int u,int v){ e[++tot].v=v,e[tot].nxt=head[u],head[u]=tot,++d[u]; e[++tot].v=u,e[tot].nxt=head[v],head[v]=tot,++d[v]; } int poww(int x,int y){ int ans=1; while(y){ if(y&1) ans=(ll)ans*x%mod; y>>=1,x=(ll)x*x%mod; } return ans; } void dfs(int x,int fa,int S){ a[x]=b[x]=0; if((1<<x)&S) return; for(int i=head[x];i;i=e[i].nxt){ int j=e[i].v; if(j==fa) continue; dfs(j,x,S); (a[x]+=a[j])%=mod,(b[x]+=b[j])%=mod; } int tmp=poww((1+mod-(ll)a[x]*d[x]%mod)%mod,mod-2); a[x]=(ll)tmp*d[x]%mod,b[x]=(ll)(1+(ll)b[x]*d[x]%mod)*tmp%mod; // cout<<x<<" "<<a[x]<<" "<<b[x]<<endl; } int main(){ #ifdef Devil_Gary freopen("in.txt","r",stdin); #endif n=read(),Q=read(),rt=read()-1; for(int i=1;i<n;i++) add(read()-1,read()-1); for(int i=0;i<n;i++) d[i]=poww(d[i],mod-2); for(int i=1;i<(1<<n);i++) bin[i]=bin[i>>1]+(i&1); for(int i=0;i<(1<<n);i++) dfs(rt,-1,i),f[i]=bin[i]&1?b[rt]:(mod-b[rt])%mod; // for(int i=0;i<(1<<n);i++) cout<<i<<" "<<f[i]<<endl; for(int j=0;j<n;j++) for(int i=0;i<(1<<n);i++) if(i&(1<<j)) (f[i]+=f[i^(1<<j)])%=mod; while(Q--){ int S=0; for(int T=read();T;T--) S|=(1<<(read()-1)); // bug(S); printf("%d\n",f[S]); } }

LOJ2542 隨機遊走 Min-Max容斥+樹上期望DP