题解 P5643 【[PKUWC2018]随机游走】
TheLostWeak · · 题解
在博客查看
大致题意: 从一个给定点出发,在一棵树上随机游走,对于相邻的每个点均有
Min-Max 容斥
访问过每个点至少一次,显然不是什么好处理的东西。
我们考虑一个叫
关于
套到这题,
经过这样的转化,似乎就可做了许多。
待定系数法
设
对于一个不属于点集
其中
乍一看,
但实际上,对于这道题,我们可以使用待定系数法。
设
两边同乘
移项,得到:
两边同除以
即:
仔细观察,可以发现,这两个式子均与父节点无关,只和子节点有关系。
上述讨论都是对于不属于点集
所以,我们只要通过一遍
如果我们把题目中给定的出发点作为根节点,由于其不存在父节点,所以
而
高维前缀和
现在我们已经知道了,对于一个点集
那么,对于给出的一个询问,我们就可以套用之前的公式:
但是,由于询问数量较多,如果对于每一次询问都去枚举子集计算答案,就会
怎么办呢?
注意到对于任意一个点集
因此,我们可以先暴力枚举
关于高维前缀和,如果你不太了解,可以看看我的这一篇博客:浅谈高维前缀和。
这样一来,对于每次询问,我们就可以直接输出答案了。
代码
#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;
}