loj2542「PKUWC2018」随机游走
题目描述
输入格式
输出格式
数据范围与提示
-
题解:
- 单点询问可以用高斯消元;
- 这个做法直接扩展到集合的话可以求出到$S$中任意一个点的期望步数;
- 如果对于一种状态,记录$S$中每个点被走到的步数$t$;
- 那么$S$中每个点都走到就是$t$的最大值,而刚刚求出来的是$t$的最小值;
- 套用最值反演:$max{S} = \sum_{T \subseteq S ,T \neq \emptyset } (-1)^{|T|-1} min{T}$;
- 现在只需要快速求出到$S$中任意一个点的期望步数,设$f_{u}$为$u$到$S$的期望步数:
- 可以得到:
- $f_{u} = \frac{1}{d_{u}} \sum_{v} f_{v} + 1 $
- 这里$v$表示和$u$相邻的点;
- 由于是一颗树,单独考虑父亲;
- $f_{u} = \frac{1}{d_{u}} f_{fa} + \frac{1}{d_{u}} \sum_{v}f_{v} + 1$ ①
- 这里$v$表示$u$的儿子节点;
- 假设已经处理好了$u$的儿子,为了能够递推,将式子写成:
- $f_{u} = A_{u}f_{fa} + B_{u}$ ②
- 那么$A_{v}$和$B_{v}$是已经处理好的,对①中的$v$用②,再对比化简的①和②:
- $$f_{u} = \frac{1}{d_{u} - \sum_{v}A_{v} } f_{fa} + \frac{d_{u} + \sum_{v}B_{v} }{d_{u} - \sum_{v} A_{v}}$$
- 这样就可以$O(n)$递推$AB$
- 用$fmt$处理反演部分的话,复杂度就是:$O(n2^n \ + \ q )$;
-
1 #include<bits/stdc++.h> 2 #define mod 998244353 3 using namespace std; 4 const int N=20,M=100010; 5 int n,q,s,S,num[1<<18],f[1<<18],o=1,hd[N],A[N],B[N],d[N],inv[M]; 6 struct Edge{int v,nt;}E[N<<1]; 7 void adde(int u,int v){ 8 E[o]=(Edge){v,hd[u]};hd[u]=o++; 9 E[o]=(Edge){u,hd[v]};hd[v]=o++; 10 } 11 inline int Inv(int x){return x<1e5?inv[x]:1ll*(mod-mod/x)*Inv(mod%x)%mod;} 12 void dfs(int u,int fa){ 13 if(S&1<<(u-1)){A[u]=B[u]=0;return;} 14 int s1=0,s2=0; 15 for(int i=hd[u];i;i=E[i].nt){ 16 int v=E[i].v; 17 if(v==fa)continue; 18 dfs(v,u); 19 s1=(s1+A[v])%mod,s2=(s2+B[v])%mod; 20 } 21 A[u]=Inv((d[u]-s1+mod)%mod); 22 B[u]=1ll*A[u]*(s2+d[u])%mod; 23 } 24 int main(){ 25 #ifndef ONLINE_JUDGE 26 freopen("loj2542.in","r",stdin); 27 freopen("loj2542.out","w",stdout); 28 #endif 29 scanf("%d%d%d",&n,&q,&s); 30 inv[1]=1; 31 for(int i=2;i<=1e5;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; 32 for(int i=1,u,v;i<n;++i){ 33 scanf("%d%d",&u,&v); 34 adde(u,v); 35 d[u]++,d[v]++; 36 } 37 for(int i=1;i<1<<n;++i){ 38 S=i;dfs(s,0); 39 num[i]=num[i>>1]+(i&1); 40 f[i]=(num[i]&1)?B[s]:(mod-B[s])%mod; 41 } 42 for(int i=0;i<n;++i) 43 for(int j=1<<i;j<1<<n;++j){ 44 if(j>>i&1)f[j]=(f[j]+f[j^(1<<i)])%mod; 45 } 46 for(int i=1,k;i<=q;++i){ 47 scanf("%d",&k); 48 S=0;for(int j=1,x;j<=k;++j)scanf("%d",&x),S^=1<<(x-1); 49 printf("%d\n",f[S]); 50 } 51 return 0; 52 }