input | output |
---|---|
6 6 2 1 1 5 6 5 |
4 |
4 3 2 3 1 |
0 |
题意:有n个人,分别编号1~n,有m个人分别表达自己的意愿,这m个人分别是1~m编号的这几个人
然后给出m个数,表示第i个人想要坐在第x个人的旁边,现在要求的是,在满足了这m个人的意愿的情况下,这张大圆桌有几种坐法
思路:我们首先来分析,对于那些想要坐在临近的人必然是互相相连的,那么他们就可以构成一棵树,那么对于总共的坐法,就是对s棵树的全排列
对于单个点的树,我们可以不用太多考虑
那么对于有子节点的树,我们要考虑环的问题
如果成环了,那么必然只剩这棵树本身,如果还有其他子树,那么必然无法满足
对于不成环的情况,我们还要注意的是对于单个节点,如果其身边必须安排的人数大于2,那么是无法满足的,因为他身边最多只能安排2个人
那么对于每棵树,这棵树不只一个节点,那么必然有两种情况
首先,如果是一条直链,例如1-2,我们有1-2和2-1两种
对于不是直链,那么肯定可以交换左右两支
什么?你说三链?这种情况之前就否定了,是0
解决了这些问题之后,这道题就解决了
#include <iostream> #include <stdio.h> #include <string.h> #include <stack> #include <queue> #include <map> #include <set> #include <vector> #include <math.h> #include <algorithm> using namespace std; #define ls 2*i #define rs 2*i+1 #define up(i,x,y) for(i=x;i<=y;i++) #define down(i,x,y) for(i=x;i>=y;i--) #define mem(a,x) memset(a,x,sizeof(a)) #define w(a) while(a) #define LL long long const double pi = acos(-1.0); #define Len 200005 #define mod 1000000007 const int INF = 0x3f3f3f3f; int vis[105][105]; int father[105]; int d[105],s[105]; LL ans; int find(int x) { if(father[x]==x) return x; return father[x] = find(father[x]); } int main() { int n,m,i,j,k,x; w(~scanf("%d%d",&n,&m)) { mem(vis,0); up(i,0,n) { father[i] = i; s[i] = 1;//这棵树的节点的情况 d[i] = 0;//d[i]是i的周围必须安排几个人 } int sum = n,flag = 0; up(i,1,m) { scanf("%d",&x); if(vis[i][x] || vis[x][i]) continue; vis[i][x] = vis[x][i] = 1; d[i]++; d[x]++; if(find(x)!=find(i)) { s[find(i)]+=s[find(x)]; father[find(x)] = father[find(i)]; sum--;//减少了一棵树 } else flag = 1;//成环 } if(n==2)//只有两个人必然只有一种坐法 { printf("1\n",ans); continue; } up(i,1,n) { if(d[i]>2)//i的旁边要安排的人数大于2,必然无法满足 break; } if(i<=n || (sum>1&&flag))//成环并且除了这棵子树还有其他子树,必然无法满足 { printf("0\n"); continue; } ans = 1; up(i,1,sum-1)//对于这些树全排列的方式有几种,但是1的位置是固定的 ans = (ans*i)%mod; up(i,1,n) { if(father[i]==i && s[i]>1)//看根节点,如果这棵树不只一个节点,那么就有两种情况 ans = (ans*2)%mod; } printf("%I64d\n",ans); } return 0; }
URAL1962:In Chinese Restaurant(并查集)
原文:http://blog.csdn.net/libin56842/article/details/45133313