loj2542 「PKUWC2018」随机游走 min-max容斥证明

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011056504/article/details/83340842

题目描述

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

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

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

答案对 998244353 取模。

输入格式

第一行三个正整数 n,Q,x。

接下来 n-1 行,每行两个正整数 (u,v) 描述一条树边。

接下来 Q 行,每行第一个数 k 表示集合大小,接下来 k 个互不相同的数表示集合 S。

输出格式

输出 Q行,每行一个非负整数表示答案。

样例输入

3 5 1
1 2
2 3
1 1
1 3
2 2 3
3 1 2 3
2 1 2

样例输出

0
4
4
4
1

题解

首先由min-max容斥
m a x ( S ) = T S ( 1 ) T + 1 m i n ( T ) max(S)=\sum_{T\subseteqq S} (-1)^{|T|+1}min(T)
证明:
考虑枚举每一个值作为最小值出现,即将S元素从小到大排序后某个元素为出现的第一个元素:
T S ( 1 ) T + 1 m i n ( T ) = i = 1 S S [ i ] j = 0 S i ( 1 ) j C S i j \sum_{T\subseteqq S} (-1)^{|T|+1}min(T)=\sum_{i=1}^{|S|} S[i]*\sum_{j=0}^{|S|-i}(-1)^{j}*C_{|S|-i}^{j}
同时
( 1 1 ) [ S i = j = 0 S i ( 1 ) j C S i j (1-1)^{[S|-i}=\sum_{j=0}^{|S|-i}(-1)^{j}*C_{|S|-i}^{j}
右边组合数就是以s[i]为最小值的子集个数,(-1)的幂就是(-1)的子集大小+1次方
只有当 i = S i=|S| 时右边的求和才为1,所以最终就是最大值。
只要你是对同一种东西求最大值和最小值,这个式子就是对的。


这题中,将询问集合最后到达的点的期望步数设为最大值,集合最先到达的点的期望步数设为最小值,它显然是满足这个式子的。
那么求从x到集合s内最先到达的点的期望步数设为 f [ x ] [ s ] f[x][s]
显然如果x属于s,那么 f [ x ] [ s ] = 0 f[x][s]=0
否则就想正常的期望一样,设y为与x相连的点,d[x]为点的度数, f [ x ] [ s ] = 1 d [ x ] f [ y ] [ s ] + 1 f[x][s]={1\over d[x]}\sum f[y][s]+1
f [ x ] [ s ] = K [ x ] f [ f a [ x ] ] [ s ] + B [ x ] f[x][s]=K[x]*f[fa[x]][s]+B[x]
如果x属于s的话,k和b就都等于0。
然后自k和b都等于0的点向上推就好了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 20
#define M 262150
#define mo 998244353
#define ll long long
using namespace std;
int n,Q,S,las[N],nxt[N*2],to[N*2],fa[N],d[N],tot=1,X,e[N],num[M];
ll f[M],K[N],B[N];
ll mi(ll a,ll b)
{
	ll c=1;
	for(;b;b/=2,a=a*a%mo) if(b%2==1) c=c*a%mo;
	return c;
}
void putin(int x,int y)
{
	nxt[++tot]=las[x];las[x]=tot;to[tot]=y;
}
void dg(int x)
{
	if(e[x-1]&S)
	{
		K[x]=B[x]=0;
		return;
	}
	K[x]=B[x]=d[x];
	for(int i=las[x];i;i=nxt[i])
	{
		int y=to[i];
		if(y==fa[x]) continue;
		fa[y]=x;
		dg(y);
		B[x]=(B[x]+B[y])%mo;
		K[x]=(K[x]-K[y]+mo)%mo;
	}
	K[x]=mi(K[x],mo-2)%mo;
	B[x]=B[x]*K[x]%mo;
}
int main()
{
	scanf("%d%d%d",&n,&Q,&X);
	fo(i,2,n)
	{
		int x,y;scanf("%d%d",&x,&y);
		putin(x,y);putin(y,x);
		d[x]++;d[y]++;
	}
	e[0]=1;fo(i,1,n) e[i]=e[i-1]*2;
	for(S=1;S<=e[n]-1;S++)
	{
		num[S]=num[S>>1]+S&1;
		dg(X);
		f[S]=B[X];
	}
	while(Q--)
	{
		int m,s=0,x;scanf("%d",&m);
		fo(i,1,m) scanf("%d",&x),s=s|e[x-1];
		ll ans=0;
		fo(i,1,e[n]-1) if((i&s)==i) ans=(ans+(num[i]%2==1?1:-1)*f[i]+mo)%mo;
		printf("%lld\n",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/u011056504/article/details/83340842