DestinHistoire

 

BZOJ-3444 最后的晚餐(并查集+组合计数)

题目描述

  \(n(1\leq n\leq 5\times 10^5)\) 个人坐成一排,有 \(m(0\leq m\leq n)\) 个条件,第 \(i\) 个条件要求 \(a_i\)\(b_i\) 相邻,求方案数。

分析

  把坐在一起看成无向边,用并查集维护关系,如果某个点的度数大于 \(2\),无解,如果形成了边数大于 \(3\) 的环,无解,可能有重边,注意去重。答案为 \(2^{cnt}·tot!\),其中 \(cnt\) 为点数大于等于 \(2\) 的连通块,\(tot\) 为总连通块数量。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int mod=989381;
int fa[N],a[N],deg[N],cnt[N];
int get(int x)
{
    if(x!=fa[x])
        return fa[x]=get(fa[x]);
    return x;
}
void merge(int x,int y)
{
    int fx=get(x),fy=get(y);
    if(fx!=fy)
        fa[fx]=fy;
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d",&x);
        scanf("%d",&y);
        a[x]=y;
        if(a[a[x]]==x)//重边
            continue;
        int fx=get(x),fy=get(y);
        if(fx==fy||deg[x]>=2||deg[y]>=2)
        {
            puts("0");
            return 0;
        }
        deg[x]++;
        deg[y]++;
        merge(x,y);
    }
    for(int i=1;i<=n;i++)
        cnt[get(i)]++;
    long long ans=1,tot=0;
    for(int i=1;i<=n;i++)
    {
        if(cnt[i]!=0)
        {
            if(cnt[i]>=2)
                ans=ans*2%mod;
            tot++;
        }
    }
    for(int i=1;i<=tot;i++)
        ans=ans*i%mod;
    cout<<ans<<endl;
    return 0;
}

posted on 2020-12-03 11:20  DestinHistoire  阅读(54)  评论(0编辑  收藏  举报

导航