GDOI 2018 Day1 小学生图论题

小学生图论题

Description

有一个点数为 n n 的竞赛图,每条边的的方向等概率随机。
现在给出m条有向链,表示每条链上的边的有向边的方向都已被钦定。
保证一个点不会出现在两条给出的链中。
求该竞赛图强连通分量的期望个数。(答案模 998244353 998244353

Data Constrains

n,m105 n , m ≤ 10 5

Solution

首先竞赛图缩完强联通分量后的是一个拓扑序唯一的,类似一条链的拓扑图,可以把这条链上的每一条边看成一个分隔,分成了前后两个点集 S S ,T,其中 S S T之间的连边的方向都是由 S S 连向T。相反的,如果在一个竞赛图中,点可以分成两个集合 S S ,T,其中 S S T之间的连边的方向都是由 S S 连向T,那这在竞赛图缩完点后必定对应一个分隔。
可以看出强联通分量的数量即为分隔数+ 1 1 ,所以我们只需求出期望的分隔数即可。

于是我们可以将答案写成1+S,TΠxS yTPx,y其中 Px,y P x , y x x 指向y的概率(由题意可知 Px,y{0,1,12} P x , y ∈ { 0 , 1 , 1 2 } )。

m m =0时,易得出 Ans=1+n1i=1Cin12i(ni) A n s = 1 + ∑ i = 1 n − 1 C n i 1 2 i ( n − i )

那当 m m 不等于0时,对于每一条给出的链,不难发现,这条链最终能分成前后两段,前一段属于 S S 集,后一段属于T集,同时我们能够发现如果一条链上的所有边并非同时属于 S S 集或同时属于T集,那么有一条边的方向就相当于钦定了。
我们考虑多项式,一条长度为 k k 的链对应的多项式G(x)=1+2x+2x2+....xk1+xk,特殊的,一个独立的点我们看成一条长度为 1 1 的链。
F(x)为所有链对应的 G(x) G ( x ) 的积,不难发现答案为 Ans=1+n1i=1(12)i(ni)[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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值