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\n");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\n",ans); 47 return 0; 48 }