JZOJ 5700. 【gdoi2018 day1】小学生图论题(graph)

4 篇文章 0 订阅
4 篇文章 0 订阅

Description

Description

Input

Input

Output

Output

Sample Input

Sample Input1

10 2
2 1 3
3 7 8 9

Sample Input2

3 0

Sample Output

Sample Output1

462789157

Sample Output2

499122179

Data Constraint

Data Constraint

Solution

  • 省选前做过一道极其类似的原题,是MYY出的,但是我当时没搞懂,好亏啊!!!

  • 我们将强连通分量缩成点后,就会形成类似一条链、前往后有连边的拓扑图。如下图:
    Solution

  • 链上的一条边就是一个分割,分成前后两个点集 S,T S , T ,边都是从 S S 连向 T 的。

  • 那么把点集对应到原竞赛图中,点可以分成两个点集 S,T S , T ,且边都是从 S S 连向 T 的,就说明缩完点后对应一个分割。

  • 我们只要求出期望的分割数即可,强连通分量个数就是分割数+1。

  • 答案可以写成:

    1+S,TxS,yTPx,y 1 + ∑ S , T ∏ x ∈ S , y ∈ T P x , y

  • 由题知,边的概率 Px,y{0,12,1} P x , y ∈ { 0 , 1 2 , 1 }

  • 于是当 m=0 m = 0 时,我们很容易得到答案:

    Ans=1+i=1n112i(ni)Cin A n s = 1 + ∑ i = 1 n − 1 1 2 i ∗ ( n − i ) ∗ C n i

  • 其中 i i 枚举的是 S 的大小,此时 S S T 的边方向确定。

  • 如果 m0 m ≠ 0 呢?考虑题目给出的每一条链,显然点的编号是与答案无关的。

  • 链可以分成前后两段,前一段属于 S S ,后一段属于 T

  • 对于一个长度为 k k 的链,我们尝试构造一个 k 阶多项式 G(x) G ( x ) xi x i

  • 如果有的点不在题目给出的链中,那么就各自当成独立的一个长度为 1 1 的链。

  • 如果链上的点同在 S 集或 T T 集中,那么使该项系数为 1 ,即 0 0 次项和 k 次项系数为 1 1

  • 否则的话,肯定有一条边的方向已经确定了,于是概率应少乘一个 12

  • 那么第 1 1 次项到第 k1 次项的系数就是 2 2

  • 于是对于每条链,我们都构造出了一个多项式:

    G(x)=1+2x+2x2+···+2xk1+xk

  • 用分治NTT将所有多项式都乘起来,时间复杂度 O(N log2N) O ( N   l o g 2 N )

  • 令所有 G(x) G ( x ) 乘起来的多项式为 F(x) F ( x ) ,则最后答案即为:

    Ans=1+i=1n112i(ni)F(x)[xi] A n s = 1 + ∑ i = 1 n − 1 1 2 i ( n − i ) ∗ F ( x ) [ x i ]

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e5+5,G=3,mo=998244353;
int a[N<<1],beg[N],end[N];
int w[N<<2],rev[N<<2],b[N<<2],c[N<<2];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline int ksm(int x,LL y)
{
    int s=1;
    while(y)
    {
        if(y&1) s=(LL)s*x%mo;
        x=(LL)x*x%mo;
        y>>=1;
    }
    return s;
}
inline void NTT(int *y,int n,int ff)
{
    for(int i=0;i<n;i++)
        if(i<rev[i]) swap(y[i],y[rev[i]]);
    for(int m=2;m<=n;m<<=1)
    {
        int h=m>>1;
        for(int i=0;i<h;i++)
        {
            int wn=ff==-1?w[n-i*(n/m)]:w[i*(n/m)];
            for(int j=i;j<n;j+=m)
            {
                int u=y[j],v=(LL)wn*y[j+h]%mo;
                y[j]=(u+v)%mo;
                y[j+h]=(u-v+mo)%mo;
            }
        }
    }
    if(ff==-1)
    {
        int inv=ksm(n,mo-2);
        for(int i=0;i<n;i++) y[i]=(LL)y[i]*inv%mo;
    }
}
void solve(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;
    solve(l,mid);
    solve(mid+1,r);
    int ln=end[l]-beg[l]+1,lm=end[mid+1]-beg[mid+1]+1;
    int len=1,ll=0;
    while(len<=ln+lm+1) len<<=1,ll++;
    for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<ll-1;
    for(int i=0;i<ln;i++) b[i]=a[beg[l]+i];
    for(int i=ln;i<=len;i++) b[i]=0;
    for(int i=0;i<lm;i++) c[i]=a[beg[mid+1]+i];
    for(int i=lm;i<=len;i++) c[i]=0;
    int num=ksm(G,(mo-1)/len);
    for(int i=w[0]=1;i<=len;i++) w[i]=(LL)w[i-1]*num%mo;
    NTT(b,len,1),NTT(c,len,1);
    for(int i=0;i<len;i++) b[i]=(LL)b[i]*c[i]%mo;
    NTT(b,len,-1);
    while(len && !b[len-1]) len--;
    end[l]=beg[l]+len-1;
    for(int i=beg[l];i<=end[l];i++) a[i]=b[i-beg[l]];
}
int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    int n=read(),m=read(),tot=0;
    for(int i=1;i<=m;i++)
    {
        int k=read();
        tot+=k;
        beg[i]=end[i-1]+1;
        end[i]=beg[i]+k;
        a[beg[i]]=a[end[i]]=1;
        for(int j=beg[i]+1;j<end[i];j++) a[j]=2;
        while(k--) read();
    }
    for(int i=tot+1;i<=n;i++)
    {
        m++;
        beg[m]=end[m-1]+1;
        end[m]=beg[m]+1;
        a[beg[m]]=a[end[m]]=1;
    }
    solve(1,m);
    int ans=1;
    for(int i=1;i<n;i++) ans=(ans+(LL)a[i+beg[1]]*ksm((mo+1)>>1,(LL)i*(n-i)))%mo;
    printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值