1. 程式人生 > >loj2542 「PKUWC2018」隨機遊走 min-max容斥證明

loj2542 「PKUWC2018」隨機遊走 min-max容斥證明

題目描述

給定一棵 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);
	}
}