小学生图论题
Description
有一个点数为
n
n
的竞赛图,每条边的的方向等概率随机。
现在给出条有向链,表示每条链上的边的有向边的方向都已被钦定。
保证一个点不会出现在两条给出的链中。
求该竞赛图强连通分量的期望个数。(答案模
998244353
998244353
)
Data Constrains
n,m≤105 n , m ≤ 10 5
Solution
首先竞赛图缩完强联通分量后的是一个拓扑序唯一的,类似一条链的拓扑图,可以把这条链上的每一条边看成一个分隔,分成了前后两个点集
S
S
,,其中
S
S
和之间的连边的方向都是由
S
S
连向。相反的,如果在一个竞赛图中,点可以分成两个集合
S
S
,,其中
S
S
和之间的连边的方向都是由
S
S
连向,那这在竞赛图缩完点后必定对应一个分隔。
可以看出强联通分量的数量即为分隔数+
1
1
,所以我们只需求出期望的分隔数即可。
于是我们可以将答案写成其中 Px,y P x , y 是 x x 指向的概率(由题意可知 Px,y∈{0,1,12} P x , y ∈ { 0 , 1 , 1 2 } )。
当 m m =时,易得出 Ans=1+∑n−1i=1Cin12i(n−i) A n s = 1 + ∑ i = 1 n − 1 C n i 1 2 i ( n − i ) 。
那当
m
m
不等于时,对于每一条给出的链,不难发现,这条链最终能分成前后两段,前一段属于
S
S
集,后一段属于集,同时我们能够发现如果一条链上的所有边并非同时属于
S
S
集或同时属于集,那么有一条边的方向就相当于钦定了。
我们考虑多项式,一条长度为
k
k
的链对应的多项式,特殊的,一个独立的点我们看成一条长度为
1
1
的链。
令为所有链对应的
G(x)
G
(
x
)
的积,不难发现答案为
Ans=1+∑n−1i=1(12)i(n−i)[xi]F(x)
A
n
s
=
1
+
∑
i
=
1
n
−
1
(
1
2
)
i
(
n
−
i
)
[
x
i
]
F
(
x
)
求
F(x)
F
(
x
)
用分治
NTT
N
T
T
,可以证明时间复杂度为
O(n log2 n)
O
(
n
l
o
g
2
n
)
。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)
using namespace std;
typedef long long ll;
const ll N=24e4,M=2e7,ZD=262144,mo=998244353;
ll be[N],en[N],a[N],b[N];
ll f[M],jc[N],nn[N],w[N<<1];
int bits[ZD<<1],ss,mm;
int len[N],x,oo,n,m;
inline int read()
{
int o=0; char ch=' ';
for(;ch<'0'||ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+(ch^48);
return o;
}
inline ll ksm(ll o,ll t)
{
ll y=1;
for(;t;t>>=1,o=o*o%mo)
if(t&1)y=y*o%mo;
return y;
}
inline void prepare()
{
w[0]=1; w[1]=ksm(3,(mo-1)/ZD);
fo(i,2,ZD)w[i]=w[i-1]*w[1]%mo;
}
inline ll mo1(ll o)
{return o>=mo?o-mo:o;}
inline ll mo2(ll o)
{return o<0?o+mo:o;}
void dft(ll *a,int sig)
{
fo(i,1,mm-2)if(i<bits[i])swap(a[i],a[bits[i]]);
for(int m=2,half=1,D=ZD>>1;m<=mm;m<<=1,half<<=1,D>>=1)
fo(i,0,half-1){
register ll u=sig==1?w[i*D]:w[ZD-i*D],v;
for(int j=i;j<mm;j+=m)
{
v=a[j+half]*u%mo;
a[j+half]=mo2(a[j]-v);
a[j]=mo1(a[j]+v);
}
}
if(sig==-1)
{
ll ny=ksm(mm,mo-2);
fo(i,0,mm-1)a[i]=a[i]*ny%mo;
}
}
void divi(int l,int r)
{
if(l==r){
be[l]=++oo;
oo=en[l]=be[l]+len[l];
fo(i,be[l],en[l])f[i]=2;
f[be[l]]=f[en[l]]=1;
return;
}
int mid=l+r>>1;
divi(l,mid); divi(mid+1,r);
int le=en[l]-be[l]+1+en[mid+1]-be[mid+1];
ss=0; mm=1;
while(mm<=le)mm<<=1,++ss; bits[0]=0;
fo(i,1,mm-1)bits[i]=(bits[i>>1]>>1)|((i&1)<<ss-1);
fo(i,0,mm-1)a[i]=b[i]=0;
fo(i,be[l],en[l])a[i-be[l]]=f[i];
fo(i,be[mid+1],en[mid+1])b[i-be[mid+1]]=f[i];
dft(a,1); dft(b,1);
fo(i,0,mm-1)a[i]=a[i]*b[i]%mo;
dft(a,-1);
be[l]=++oo; oo=en[l]=be[l]+le-1;
fo(i,be[l],en[l])f[i]=a[i-be[l]];
}
int main()
{
cin>>n>>m;
int zh=0;
fo(i,1,m){
len[i]=read(); zh=zh+len[i];
fo(l,1,len[i])x=read();
}
fo(i,m+1,m+n-zh)len[i]=1;
m+=n-zh; prepare();
divi(1,m);
ll ans=1;
fo(i,1,n-1)ans=(ans+f[i+be[1]]*ksm((mo+1)/2,(ll)i*(n-i)))%mo;
cout<<ans;
}