1 条题解

  • 0
    @ 2025-8-24 22:12:53

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar TheLostWeak
    **

    搬运于2025-08-24 22:12:53,当前版本为作者最后更新于2019-12-20 13:23:35,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    在博客查看

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

    MinMaxMin-Max容斥

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

    我们考虑一个叫MinMaxMin-Max容斥的东西。

    关于MinMaxMin-Max容斥,有这样一个公式:

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

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

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

    待定系数法

    fif_i为从ii出发,到达点集TT一个点的期望步数。

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

    fi=1degi(ffai+fj)+1f_i=\frac1{deg_i}(f_{fa_i}+\sum f_j)+1

    其中jj满足jjii的子节点。

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

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

    fi=kiffai+bif_i=k_if_{fa_i}+b_i,根据上面的转移方程,我们知道:

    fi=1degi(ffai+(kjfi+bj))+1f_i=\frac1{deg_i}(f_{fa_i}+\sum(k_jf_i+b_j))+1

    两边同乘degideg_i,便可得:

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

    移项,得到:

    (degikj)fi=ffai+bj+degi(deg_i-\sum k_j)\cdot f_i=f_{fa_i}+\sum b_j+deg_i

    两边同除以degikjdeg_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} $$

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

    上述讨论都是对于不属于点集TT中的ii的,而对于点集TT中的ii,由于fi=0f_i=0,所以ki=bi=0k_i=b_i=0

    所以,我们只要通过一遍dfsdfs,就可以求出所有点的kik_ibib_i了。

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

    frtf_{rt}也正是我们所要求的到达点集TT一个点的期望步数。

    高维前缀和

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

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

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

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

    怎么办呢?

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

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

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

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

    代码

    #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

    信息

    ID
    4632
    时间
    1000ms
    内存
    500MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者