OCPC2023 I. DAG Generation
题目传送门
题意
给你一种DAG生成方式,问生成两张DAG相同的概率是多少。
生成方式为,一开始有\(A,B\)两个集合,A为空集,B中有\(1-n\)每个节点,每次从B中随机取出一个点,然后在A中随机取出一个子集,把子集中的每个点往B中取出的点连一条有向边,然后把取出点放入A。
题解
我们不妨认为第一次生成取出点的顺序就是1,2,3.......n,第二次任意顺序取出点,题目转化为这两次生成相同的概率。显然取出点的顺序不同还是有可能生成同一张图。
对于第\(i\)个取出的点,它可以对前\(i-1\)个点选或者不选连边,一共有\(2^{i-1}\)种不同的选择,所以对于图1,一共有\(2^0*2^1...+2^{n-1}\)种不同的生成方式,每一种生成方式出现的概率是完全相同的,图2在图1的基础上,取出点顺序是不确定, 每一种取点顺序都有那么多生成方式,总的生成方式数就是\(2^0*2^1...+2^{n-1}*n!\), 两个图一起生成,就有\((2^0*2^1...+2^{n-1})^2*n!\)种不同的生成方式.
所以答案就是两张图一起生成时的
因为每一种不同的生成方式等概率出现.
分母已经知道,关键时分子怎么求, 自然想到我们可以对于图1的每一种生成方式,算出图2种有多少生成方式可以生成同样的图,然后求和就好了。但是这个东西并不好求,由于图2的一种生成方式最多对应图1的一种方式,因为图1不同方式生成的图一定不同, 所以我们考虑反过来计算图2 种有多少生成方式可以在图1中找到对应。
于是问题就转化成了,图2有多少生成方式可以生成一张DAG,使得DAG中的每一个点只存在由编号比它小的点到达的有向边。
考虑这个东西可以dp,\(F_i\)表示\(i\)个点有多少生成方式,然后考虑插入\(i+1\)号点转移,我们先枚举插入的位置,前面已经有\(i\)个数,一共有\(i+1\)种不同的插入位置,不同的插入位置对应图2生成时不同的取点顺序,然后再乘上插入的这个点的决策数,由于插入的这个点比之前所有点都大,所以可以选择任意的比它先取出的点,如果这个点插入j位置,那么就说明前\(j-1\)个点都可以选,一共\(2^{j-1}\)方案。所以对于任意一个\(i\)个点的生成方案,插入\(i+1\)号点后一共会有$(20+21....+2^i)种不同方案,有:
而这就是分子。
实现
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdlib>
#define ll long long
using namespace std;
int read(){
int num=0, flag=1; char c=getchar();
while(!isdigit(c) && c!='-') c=getchar();
if(c == '-') flag=-1, c=getchar();
while(isdigit(c)) num=num*10+c-'0', c=getchar();
return num*flag;
}
const ll mod = 1e9+9;
const int N = 1e5+1000;
ll ksm(ll x, ll k){
if(k == 0) return 1;
if(k == 1) return x;
ll res = ksm(x, k/2); res=res*res%mod;
if(k%2) res = res*x%mod;
return res;
}
ll add2[N], mul2[N], fac[N];
ll F[N];
int main(){
fac[0]=1; for(int i=1; i<N; i++) fac[i] = fac[i-1]*i%mod;
add2[0]=1; mul2[0]=1;
for(int i=1; i<N; i++) add2[i] = (add2[i-1] + ksm(2, i))%mod;
for(int i=1; i<N; i++) mul2[i] = (mul2[i-1] * ksm(2, i))%mod;
F[1] = 1;
for(int i=2; i<N; i++) F[i] = F[i-1]*add2[i-1]%mod;
int T = read();
while(T--){
ll n=read();
ll ans = F[n] * ksm(mul2[n-1]*mul2[n-1]%mod*fac[n]%mod, mod-2) %mod;
ans = (1-ans)%mod + mod;
printf("%lld\n", ans%mod);
}
return 0;
}