点此看题面

大致题意: 从一个给定点出发,在一棵树上随机游走,对于相邻的每个点均有\(\frac 1{deg}\)的概率前往。多组询问,每次给出一个点集,求期望经过多少步能够访问过点集内所有点至少一次。

\(Min-Max\)容斥

访问过每个点至少一次,显然不是什么好处理的东西。

我们考虑一个叫\(Min-Max\)容斥的东西。

关于\(Min-Max\)容斥,有这样一个公式:

\[E(max(S))=\sum_{T∈S}(-1)^{|T|+1}E(min(T))
\]

套到这题,\(E(max(S))\)就是访问点集\(S\)所有点至少一次的期望步数,\(E(min(T))\)就是到达点集\(T\)一个点的期望步数。

经过这样的转化,似乎就可做了许多。

待定系数法

设\(f_i\)为从\(i\)出发,到达点集\(T\)一个点的期望步数。

对于一个不属于点集\(T\)的\(i\),设其度数为\(deg_i\),因为它对于相邻的每个点均有\(\frac 1{deg_i}\)的概率前往,显然有:

\[f_i=\frac1{deg_i}(f_{fa_i}+\sum f_j)+1
\]

其中\(j\)满足\(j\)是\(i\)的子节点。

乍一看,\(f_i\)既会从子节点转移,又会从父节点转移,转移出现了环,似乎需要高斯消元

但实际上,对于这道题,我们可以使用待定系数法

设\(f_i=k_if_{fa_i}+b_i\),根据上面的转移方程,我们知道:

\[f_i=\frac1{deg_i}(f_{fa_i}+\sum(k_jf_i+b_j))+1
\]

两边同乘\(deg_i\),便可得:

\[deg_i\cdot f_i=f_{fa_i}+(\sum k_j)f_i+\sum b_j+deg_i
\]

移项,得到:

\[(deg_i-\sum k_j)\cdot f_i=f_{fa_i}+\sum b_j+deg_i
\]

两边同除以\(deg_i-\sum k_j\),可得:

\[f_i=\frac1{deg_i-\sum k_j}f_{fa_i}+\frac{deg_i+\sum b_j}{deg_i-\sum k_j}
\]

即:

\[k_i=\frac 1{deg_i-\sum k_j},b_i=\frac{deg_i+\sum b_j}{deg_i-\sum k_j}
\]

仔细观察,可以发现,这两个式子均与父节点无关,只和子节点有关系。

上述讨论都是对于不属于点集\(T\)中的\(i\)的,而对于点集\(T\)中的\(i\),由于\(f_i=0\),所以\(k_i=b_i=0\)。

所以,我们只要通过一遍\(dfs\),就可以求出所有点的\(k_i\)和\(b_i\)了。

如果我们把题目中给定的出发点作为根节点,由于其不存在父节点,所以\(f_{rt}=b_{rt}\)。

而\(f_{rt}\)也正是我们所要求的到达点集\(T\)一个点的期望步数。

高维前缀和

现在我们已经知道了,对于一个点集\(T\),如何求出到达点集\(T\)一个点的期望步数。

那么,对于给出的一个询问,我们就可以套用之前的公式:

\[E(max(S))=\sum_{T∈S}(-1)^{|T|+1}E(min(T))
\]

但是,由于询问数量较多,如果对于每一次询问都去枚举子集计算答案,就会\(TLE\)。

怎么办呢?

注意到对于任意一个点集\(T\),\((-1)^{|T|+1}E(min(T))\)完全与\(S\)没有半点关系。

因此,我们可以先暴力枚举\(T\),求出每一个\(T\)的答案,然后用高维前缀和来预处理\(S\)的答案。

关于高维前缀和,如果你不太了解,可以看看我的这一篇博客:浅谈高维前缀和

这样一来,对于每次询问,我们就可以直接输出答案了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 18
#define X 998244353
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,rt,ee,lnk[N+5];struct edge {int to,nxt;}e[2*N+5];
I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
namespace DP//树形DP预处理答案
{
int p[N+5],k[N+5],b[N+5],s[1<<N],g[1<<N];
I void dfs(CI x,CI lst)//DP
{
if(p[x]) return (void)(k[x]=b[x]=0);//如果在点集T中,直接返回
RI i,d=0,sk=0,sb=0;for(i=lnk[x];i;i=e[i].nxt) ++d,//枚举子节点的同时统计度数
e[i].to^lst&&(dfs(e[i].to,x),Inc(sk,k[e[i].to]),Inc(sb,b[e[i].to]));//枚举子节点,并统计k与b的和
k[x]=Qpow((d-sk+X)%X,X-2),b[x]=1LL*(d+sb)*Qpow((d-sk+X)%X,X-2)%X;//计算当前节点k与b的值
}
I void Init()
{
RI i,j,t=1<<n;for(i=1;i^t;++i)//枚举点集T
{for(j=1;j<=n;++j) p[j]=(i>>j-1)&1;dfs(rt,0),s[i]=b[rt];}//求出到达点集T一个点的期望步数
}
I void Calc()
{
RI i,j,t=1<<n;for(i=1;i^t;++i) !(g[i]=g[i>>1]^(i&1))&&(s[i]=X-s[i]);//乘上容斥系数
for(j=1;j<=n;++j) for(i=1;i^t;++i) (i>>j-1)&1&&Inc(s[i],s[i-(1<<j-1)]);//高维前缀和
}
}
int main()
{
RI Qt,i,x,y,t;scanf("%d%d%d",&n,&Qt,&rt);
for(i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);DP::Init(),DP::Calc();//读入+预处理
W(Qt--)//处理询问
{
for(scanf("%d",&x),t=0,i=1;i<=x;++i) scanf("%d",&y),t|=1<<y-1;//读入,状压
printf("%d\n",DP::s[t]);//直接输出答案
}return 0;
}

最新文章

  1. require,include,require_once,include_once的区别
  2. POJ 3304 Segments (直线与线段是否相交)
  3. c# 4.0新特性一览
  4. Java从Jar文件中动态加载类
  5. Linux 最大进程数
  6. css 元素居中方法
  7. CSS小例收藏
  8. Android中启动页ViewPager和ViewFlipper带指示器
  9. HDU1081 最大字段和 压缩数组
  10. 我是如何将博客转成PDF的
  11. Python之file
  12. 自身使用的springboot项目中比较全的pom.xml
  13. 胜利大逃亡,bfs,广度优先搜索
  14. scrapy关键字爬取百度图库(一)
  15. 独立出properties的mybatis连接池
  16. ldap集成nginx
  17. mui-图文列表 图片大小问题
  18. zoj2607
  19. Eigen教程(7)
  20. windows7所有版本迅雷地址下载集合(含32位和64位) - imsoft.cnblogs

热门文章

  1. 【docker】使用学习
  2. C语言每日一练——第2题
  3. ESD选型
  4. jquery查出元素名称
  5. 自定义滚动条(Custom ScrollBar)
  6. 手把手教你DIY尼康ML-L3红外遥控器
  7. 设备数据通过Azure Functions 推送到 Power BI 数据大屏进行展示(2.Azure Functions实战)
  8. 【每天一题】LeetCode 0067. 二进制求和
  9. git到GitHub的操作和遇到的一些问题
  10. Androi O Automotive 介绍