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) 编辑 收藏 举报