bzoj3444: 最后的晚餐(并查集+组合数学)

3444: 最后的晚餐

题目:传送门 


题解:

   考虑有解的情况:

   直接上并查集,同一个联通块里的人一定要坐在一起的。不难发现其实对于每个联通块最多就只有两种排列方式,那就直接把大于等于两个人的联通块先去做快速幂,然后直接看成cnt个数来全排列。

   对付无解的情况:

   1、不难想到,有环就无解。

   2、深入想想,一个人最多和两个暗恋对象坐在一起,那么如果一个人暗恋三个人或被三个人暗恋...那也无解

   3、最后一个有点坑,题目说没有自恋,但是没有说两情相悦不OK啊,那就要特判了ORZ

   吐槽:CC问我奇环合不合法...笑眯眯嘿嘿嘿,应该支持同性恋???

    


代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cmath>
 5 #include<algorithm>
 6 #define mod 989381
 7 using namespace std;
 8 typedef long long LL;
 9 int n,m,x,y,fa[510000];
10 int findfa(int x){if(fa[x]!=x)fa[x]=findfa(fa[x]);return fa[x];}
11 LL p_m(LL a,int b)
12 {
13     LL ans=1;
14     while(b)
15     {
16         if(b%2==1)ans=ans*a%mod;
17         a=a*a%mod;b/=2;
18     }
19     return ans;
20 }
21 int d[510000],tot[510000];LL cnt;
22 int mp[510000];
23 int main()
24 {
25     memset(d,0,sizeof(d));memset(mp,0,sizeof(mp));
26     scanf("%d%d",&n,&m);bool bk=true;
27     for(int i=1;i<=n;i++)fa[i]=i;
28     for(int i=1;i<=m;i++)
29     {
30         scanf("%d%d",&x,&y);if(mp[y]==x || mp[x]==y)continue;mp[x]=y;
31         if(d[x]>=2 || d[y]>=2){bk=false;break;}
32         int fx=findfa(x),fy=findfa(y);
33         if(fx!=fy)fa[fy]=fx;else {bk=false;break;}
34         d[x]++;d[y]++;
35     }
36     cnt=0;memset(tot,0,sizeof(tot));LL k=0;
37     if(bk==false){printf("0
");return 0;}
38     for(int i=1;i<=n;i++)fa[i]=findfa(i);
39     for(int i=1;i<=n;i++)
40     {
41         if(fa[i]==i)cnt++;
42         tot[fa[i]]++;
43     }
44     for(int i=1;i<=n;i++)if(tot[i]==1)k++;
45     LL ans=p_m(2,cnt-k);for(LL i=1;i<=cnt;i++)ans=ans*i%mod;
46     printf("%lld
",ans);
47     return 0;
48 }
原文地址:https://www.cnblogs.com/CHerish_OI/p/8661566.html