题目描述

  有一棵 \(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. Xamarin 学习笔记 - Layout(布局)

    本文翻译自CodeProject文章:https://www.codeproject.com/Articles/1227733/Xamarin-Notes-Xamarin-Forms-Layouts ...

  2. 关于Keychain

    1.Keychain 浅析 2.iOS的密码管理系统 Keychain的介绍和使用 3.iOS开发中,唯一标识的解决方案之keyChain+UUID

  3. Visual Studio无法调试

    一.最近Visual studio调试不起来,运行完报错 二.解决方法 打开  调试>>>>选项>>>>常规>>>对ASP.NET启用 ...

  4. Bootstrap-table表格初始化表格数据

    一.项目说明 ①此项目是ASP.NET项目,开发语言是C# ②bootstrap-table使用需要下载对应的css和js插件 ③具体详情还需查看api文档 二.前端代码 <div class= ...

  5. [20190401]隐含参数_mutex_spin_count.txt

    [20190401]隐含参数_mutex_spin_count.txt --//上午做了一些测试关于semtimedop函数调用,发现自己上个星期在一些问题上理解错误.--//相关链接:--//htt ...

  6. call、apply的作用和区别是什么?

    call().apply()的区别: 相同点: 1.call()和apply()都可以用来间接调用函数,都可以显式调用所需的this.即,任何函数可以作为任何对象的方法来调用. 2.两个方法都可以指定 ...

  7. 我的第一个python web开发框架(29)——定制ORM(五)

    接下来我们要封装的是修改记录模块. 先上产品信息编辑接口代码 @put('/api/product/<id:int>/') def callback(id): ""&q ...

  8. Linux Collection:系统信息和配置

    PAS Linux系统配置相关文件 /etc 目录中是系统和各类软件的配置文件 /var/log 系统日志 /proc 系统运行信息 PAS /boot空间不足 查看内核 dpkg --get-sel ...

  9. 日志学习系列(二)——Log4net的实例

    一.log4net简单实例创建步骤如下 1.第一步:在项目中添加对log4net.dll的引用,这里引用版本是2.0.8.0 2.第二步:程序启动时读取log4net的配置文件. 读取log4net的 ...

  10. element 关闭弹窗时清空表单信息

    关闭弹窗时清空表单信息: // 弹框关闭时清空信息 closeDialog () { this.$nextTick(() => { this.$refs['createModelForm'].c ...