BZOJ-3444 最後的晚餐(並查集+組合計數)
阿新 • • 發佈:2020-12-03
題目描述
\(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; }