题目描述

  有一棵 \(n\) 个点的树。你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去。

  有 \(q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一直随机游走,直到点集 \(S\) 中所有点都至少经过一次的话,期望游走几步。

  特别地,点 \(x\)(即起点)视为一开始就被经过了一次。

  答案对 \(998244353\) 取模。

题解

  这道题要求点集 \(S\) 中所有点都至少经过一次的期望步数,直接做不好做,要先用一个 min-max 容斥转换成走到点集 \(S\) 中第一个点的期望步数:

\[\max(S)=\sum_{T\subseteq S,T\neq \varnothing}{(-1)}^{|T|+1}\min(T)
\]

  然后就可以列方程高斯消元了。

  \(f_i\) 表示从 \(i\)走到最近的点所需要的最小步数。

\[\begin{align}
f_i&=1+\frac{1}{d_i}f_{fa}+\frac{1}{d_i}\sum_v f_v
\end{align}
\]

  直接高斯消元是 \(O(n^3)\) 的,但是我们可以用一些技巧把这个过程加速到 \(O(n\log p)\)(\(\log p\) 来自求逆元)。

  设 \(f_i=a_if_{fa}+b_i\)。特别的,如果 \(i\in S\),那么\(a_i=0,b_i=0\)。

\[\begin{align}
f_i&=1+\frac{1}{d_i}f_{fa}+\frac{1}{d_i}\sum_{v}(a_vf_i+b_v)\\
&=1+\frac{1}{d_i}f_{fa}+\frac{1}{d_i}(\sum_{v}a_vf_i+\sum_{v}b_v)\\
d_if_i&=d_i+f_{fa}+\sum_{v}a_vf_i+\sum_{v}b_v\\
(d_i-\sum_{v}a_v)f_i&=d_i+f_{fa}+\sum_{v}b_v\\
f_i&=\frac{1}{d_i-\sum_{v}a_v}f_{fa}+\frac{\sum_{v}b_v+d_i}{d_i-\sum_{v}a_v}\\
\end{align}
\]

  这样就可以从下往上递推得到\(a_i,b_i\)。

  那么答案就是 \(b_x\)

  然后就可以轻松算出询问每一个集合的答案了。

  时间复杂度:\(O(n2^n\log p+qn)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
if(a>b)
swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
int rd()
{
int s=0,c,b=0;
while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c=='-')
{
c=getchar();
b=1;
}
do
{
s=s*10+c-'0';
}
while((c=getchar())>='0'&&c<='9');
return b?-s:s;
}
void put(int x)
{
if(!x)
{
putchar('0');
return;
}
static int c[20];
int t=0;
while(x)
{
c[++t]=x%10;
x/=10;
}
while(t)
putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
if(b<a)
{
a=b;
return 1;
}
return 0;
}
int upmax(int &a,int b)
{
if(b>a)
{
a=b;
return 1;
}
return 0;
}
const ll p=998244353;
ll fp(ll a,ll b)
{
ll s=1;
for(;b;b>>=1,a=a*a%p)
if(b&1)
s=s*a%p;
return s;
}
ll f[100];
ll g[100];
vector<int> a[100];
int d[100];
int b[100];
void dfs(int x,int fa)
{
if(b[x])
{
f[x]=g[x]=0;
return;
}
f[x]=0;
g[x]=d[x];
ll k=d[x];
for(auto v:a[x])
if(v!=fa)
{
dfs(v,x);
k=(k-f[v])%p;
g[x]=(g[x]+g[v])%p;
}
k=fp(k,p-2);
f[x]=k;
g[x]=g[x]*k%p;
}
int n,q,rt;
ll s[1<<20];
int main()
{
open("loj2542");
scanf("%d%d%d",&n,&q,&rt);
int x,y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
a[x].push_back(y);
a[y].push_back(x);
d[x]++;
d[y]++;
}
for(int i=1;i<1<<n;i++)
{
int num=0;
for(int j=1;j<=n;j++)
{
b[j]=((i>>(j-1))&1);
num+=b[j];
}
dfs(rt,0);
s[i]=g[rt];
if(!(num&1))
s[i]=-s[i];
}
for(int i=1;i<=n;i++)
for(int j=0;j<1<<n;j++)
if((j>>(i-1))&1)
s[j]=(s[j]+s[j^(1<<(i-1))])%p;
int k;
for(int i=1;i<=q;i++)
{
scanf("%d",&k);
x=0;
for(int i=1;i<=k;i++)
{
scanf("%d",&y);
x|=1<<(y-1);
}
printf("%lld\n",(s[x]+p)%p);
}
return 0;
}

【LOJ2542】【PKUWC 2018】随机游走 min-max容斥 树上高斯消元的更多相关文章

  1. loj2542 「PKUWC2018」随机游走 MinMax 容斥+树上高斯消元+状压 DP

    题目传送门 https://loj.ac/problem/2542 题解 肯定一眼 MinMax 容斥吧. 然后问题就转化为,给定一个集合 \(S\),问期望情况下多少步可以走到 \(S\) 中的点. ...

  2. 「PKUWC2018」随机游走(min-max容斥+FWT)

    「PKUWC2018」随机游走(min-max容斥+FWT) 以后题目都换成这种「」形式啦,我觉得好看. 做过重返现世的应该看到就想到 \(min-max\) 容斥了吧. 没错,我是先学扩展形式再学特 ...

  3. 【LOJ#2542】[PKUWC2018]随机游走(min-max容斥,动态规划)

    [LOJ#2542][PKUWC2018]随机游走(min-max容斥,动态规划) 题面 LOJ 题解 很明显,要求的东西可以很容易的进行\(min-max\)容斥,那么转为求集合的\(min\). ...

  4. 洛谷 P5643 - [PKUWC2018]随机游走(Min-Max 容斥+FWT+树上高斯消元,hot tea)

    题面传送门 一道挺综合的 hot tea,放到 PKUWC 的 D2T2 还挺喜闻乐见的( 首先我们考虑怎样对一个固定的集合 \(S\) 计算答案,注意到我们要求的是一个形如 \(E(\max(S)) ...

  5. [PKUWC 2018]随机游走

    Description 题库链接 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次询问给定一个集合 \(S\) ...

  6. 【洛谷5643】[PKUWC2018] 随机游走(Min-Max容斥+待定系数法+高维前缀和)

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

  7. BZOJ3141 Hnoi2013 游走 【概率DP】【高斯消元】*

    BZOJ3141 Hnoi2013 Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点 ...

  8. 【BZOJ3143】【HNOI2013】游走 && 【BZOJ3270】博物馆 【高斯消元+概率期望】

    刚学完 高斯消元,我们来做几道题吧! T1:[BZOJ3143][HNOI2013]游走 Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小 ...

  9. LOJ2542 PKUWC2018 随机游走 min-max容斥、树上高斯消元、高维前缀和、期望

    传送门 那么除了D1T3,PKUWC2018就更完了(斗地主这种全场0分的题怎么会做啊) 发现我们要求的是所有点中到达时间的最大值的期望,\(n\)又很小,考虑min-max容斥 那么我们要求从\(x ...

随机推荐

  1. 基于element-tree-table树型表格点击节点请求数据展开树型表格

    效果: 引用CSS.JS: Vue.element-ui.Axios treeTable: https://github.com/ProsperLee/element-tree-grid 模拟根据父i ...

  2. vue实现表计监测界面

    已经好几个月没有更新博客了,因为最近太忙,忙得连写博客的时间都没有.上班赶项目开启996模式,下班要去练车考驾照,一边还在赶书稿,一边还接了私活.不由得感叹:年纪大了,再也经不起那么折腾..... 每 ...

  3. Android为TV端助力linux命令

    从命令行push到系统目录,用户组是root,而代码里面的是个人用户组,所以要把你push进去的东西改成跟你APK一样的用户组,并且chmod -R 777 文件名修改文件的权限 修改用户组chown ...

  4. Linux查看分区文件系统类型总结

    在Linux 中如何查看分区的文件系统类型,下面总结几种查看分区文件系统类型的方法. 1: df -T 命令查看 这个是最简单的命令,文件系统类型在Type列输出.只可以查看已经挂载的分区和文件系统类 ...

  5. cannot be run because the QueueReader subsystem failed to load

    前阵子一数据库服务器的事务日志开始暴增,当时使用下面脚本检查发现该数据库的log_reuse_wait_desc 一直处于REPLICATION状态, 也就是说在事务复制过程中,与发布相关的事务仍未传 ...

  6. 取消导航栏navigationBar的半透明/毛玻璃效果

    iOS 7.0以上的系统,导航栏默认有毛玻璃效果,遮住了颜色 原因是7.0以上的系统,导航栏默认有毛玻璃效果,遮住了颜色,取消掉这个效果就行了. if( ([[[UIDevice currentDev ...

  7. 用SQL语句实现:当A列大于B列时选择A列否则选择B列,当B列大于C列时选择B列否则选择C列。

    数据库中有A B C三列,用SQL语句实现:当A列大于B列时选择A列否则选择B列,当B列大于C列时选择B列否则选择C列. 方法一: select (case when a>b then a el ...

  8. 【PAT】B1016 部分A+B

    水题 以字符和字符串形式储存输入,比较,计算出两个个数的D的个数,用for循环拼成P,相加得出结果 #include<stdio.h> int main(){ char A[20],DA, ...

  9. apache https配置【转】

    博文来源:apache https配置 参考博文:apache.nginx配置自签名证书 1.  确认是否安装ssl模块 是否有mod_ssl.so文件 2.  生成证书和密钥 linux下 步骤1: ...

  10. LeetCode算法题-Baseball Game(Java实现)

    这是悦乐书的第288次更新,第305篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第156题(顺位题号是682).你现在是棒球比赛点记录器.给定一个字符串列表,每个字符串 ...