Description
给出一张 n个点的完全图,现在要给这个完全图的每一条边随机定向成一个有向图。对于一条边
(i,j)(i<j)
(
i
,
j
)
(
i
<
j
)
,这条边的方向是 i 到 j 的概率是
numi,j10000
n
u
m
i
,
j
10000
,
numi,j
n
u
m
i
,
j
指这条边旁边的数字,否则就是 j 到 i。在随机定向后,设这张有向图的强连通分量数目为 x,求
x×10000n(n−1)
x
×
10000
n
(
n
−
1
)
的期望,可以证明该期望值一定是一个整数。因为答案可能很大,所以只需要求出这个答案对 998244353取模后的结果。
只有m条边旁边有数字,其他边的概率都是1/2
n<=38,m<=19,w<=10000
Solution
完全图每条边定向,那么它就变成了一个竞赛图
竞赛图有一个非常好的性质,将它强联通分量缩点后,剩下的DAG的拓扑序列构成了唯一确定的一条链,链上每个强联通分量都向后面的所有强联通分量有边
(我也不会证)
我们定义割集(只在这道题中)为这条链的某一个间隙(包括第一个强联通分量前面的和最后一个后面的),这是一个边集(可以为空),它将所有点分成S和T两个集合,满足所有S与T之前的边方向都是从S到T
容易发现我们要求的强联通分量的个数就是割集个数-1
那么我们不妨直接统计割集
一种2^n*m的做法就出来了,枚举S集,计算边都是从这里连出去的概率
然而事实上我们发现只有m条边的概率不是1/2,我们将这些边成为特殊边
对于只有特殊边的割集,它们的贡献我们是可以直接计算的。
因此对于没有特殊边的情况,所有大小相同的S集对应的割集的概率都是一样的
考虑有特殊边的情况
我们先把所有边看做不是特殊边计入答案
然后再将特殊边的概率都乘上2,那就把之前的1/2消掉了
如果直接枚举特殊边的状态是不行的,有可能特殊边之间共用的端点会互相影响
那我们可以将特殊边组成的联通块弄出来,对于每个联通块枚举其中的点是在S中还是在T中
如果一个点没有与任何特殊边相连,那么将它看做单独一个点的联通块
容易发现这样的联通块点数最多为m+1
然后直接计算这个联通块的答案
最后,再将每个联通块的答案通过背包合并在一起,即可得到每种大小的联通块的答案了
具体可以参考标程
复杂度
O(2m+1∗n+n2)
O
(
2
m
+
1
∗
n
+
n
2
)
Code
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 39
#define LL long long
#define mo 998244353
using namespace std;
LL a[N][N],ans,cf,ny,f[N],g[N];
int n,m,cnt,a1[N][N],fr[N][2],cf2[22],a2[N][N],cny2[N*N];
bool bz[N][N],b[N];
void dfs(int k,int c)
{
fr[k][0]=c;
a1[c][++a1[c][0]]=k;
fr[k][1]=a1[c][0];
fo(i,1,n) if(!fr[i][0]&&a[k][i]!=5000) dfs(i,c);
}
int main()
{
cin>>n>>m;
if(n==1)
{
printf("1\n");
return 0;
}
fo(i,1,n) fo(j,i+1,n) a[i][j]=a[j][i]=5000;
fo(i,1,m)
{
int x,y;
scanf("%d%d",&x,&y);
a2[x][++a2[x][0]]=y;
a2[y][++a2[y][0]]=x;
scanf("%lld",&a[x][y]);
a[y][x]=10000-a[x][y];
}
cf=1;
ny=796898467;
fo(i,1,n*(n-1)) cf=cf*(LL)10000%mo;
ans=0;
cf2[0]=1;
fo(i,1,m+1) cf2[i]=cf2[i-1]*2;
fo(i,1,n)
{
if(!fr[i][0]) dfs(i,++cnt);
}
g[0]=1;
fo(i,1,cnt)
{
fo(j,0,n) f[j]=0;
fo(l,0,cf2[a1[i][0]]-1)
{
int ct=0;
LL s=1;
fo(q,1,a1[i][0])
{
if(cf2[q-1]&l)
{
ct++;
int k=a1[i][q];
fo(t,1,a2[k][0])
{
int p=a2[k][t];
if(fr[p][0]!=i) continue;
if(!(cf2[fr[p][1]-1]&l)) s=s*(LL)2%mo*a[k][p]%mo*ny%mo;
}
}
}
f[ct]=(f[ct]+s)%mo;
}
fod(j,n,0)
{
LL v=0;
fo(k,0,j)
{
v=(v+g[j-k]*f[k])%mo;
}
g[j]=v;
}
}
ans=0;
LL ny2=499122177;
cny2[0]=1;
fo(i,1,n*n) cny2[i]=cny2[i-1]*ny2%mo;
fo(i,1,n)
{
ans=(ans+g[i]*cny2[i*(n-i)]%mo)%mo;
}
printf("%lld\n",ans*cf%mo);
}