题解 CF1842H【Tenzing and Random Real Numbers】

发布时间 2023-07-16 18:44:06作者: caijianhong

看了题解。好难受,想用积分求概率,算了半天。发现没啥规律,不是不能算,就是太可怕了。

Problem

\(n\)\([0,1]\) 范围内的均匀随机变量 \(x_{1\cdots n}\)\(m\) 条限制,每条限制形如 \(x_i+x_j\le 1\)\(x_i+x_j\ge 1\)。请你求出所有限制均满足的概率。对 \(998244353\) 取模。

\(1\le n\le 20,\ 0\le m\le n^2+n\)

Solution

\(y_i=x_i-\frac{1}{2}\)。这样的好处是,消去常数,我们有如下的性质:

  • \(x_i+x_j<1\Rightarrow y_i+y_j<0\),那么它们同为负或者负数的绝对值大于正数。不妨 \(|y_i|<|y_j|\),那么就是 \(y_j<0\)
  • 将小于换成大于也一样。

这启发我们按照 \(|y_i|\) 大小考虑。考虑一个集合 \(S\),现在加入 \(y_i\),那么发现 \(y_i\) 能不能加入,符号是正是负,只和它自己有关。那么状压 DP 就是了。

话说这里有个小细节:\(y_i\)\(y_1,y_2,\cdots,y_{i-1}\) 都大的概率是 \(\frac{1}{i}\)。可以考虑算方案数,最后除以总方案数的时候会发现有个阶乘,那么把这个阶乘分发到每个加入的地方即可。或者好像有神秘微积分证明。

Code

点击查看代码
// LUOGU_RID: 115883767

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=998244353;
LL red(LL&x){x%=P;}
LL mod(LL x){return (x%P+P)%P;}
int getbit(int k){return 1<<k;}
bool conbit(int x,int k){return x&getbit(k);}
int n,m;
LL qpow(LL a,LL b,int p=P){LL r=1;for(;b;b>>=1,a=a*a%p) if(b&1) r=r*a%p; return r;}
LL inv[30],f[1<<20],Ele[30],Ege[30];
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	for(int i=1;i<=25;i++) inv[i]=qpow(i,P-2,P);
	scanf("%d%d",&n,&m);
	for(int i=1,t,u,v;i<=m;i++){
		scanf("%d%d%d",&t,&u,&v),u--,v--;
		if(!t) Ele[u]|=1<<v,Ele[v]|=1<<u;
		else Ege[u]|=1<<v,Ege[v]|=1<<u;
	}
	f[0]=1;
	for(int S=0;S<1<<n;S++){
		for(int i=0;i<n;i++){
			if(conbit(S,i)) continue;
			if((Ege[i]&(S|1<<i))==0) red(f[S|1<<i]+=f[S]);
			if((Ele[i]&(S|1<<i))==0) red(f[S|1<<i]+=f[S]);
		}
	}
	LL ans=f[(1<<n)-1];
	for(int i=1;i<=n;i++) red(ans*=inv[i]*inv[2]%P);
	printf("%lld\n",ans);
	return 0;
}