In Chinese Restaurant
64-bit integer IO format: %lld Java class name: (Any)
Input
Output
Sample Input
input | output |
---|---|
6 6 2 1 1 5 6 5 | 4 |
4 3 2 3 1 | 0 |
排列组合、并查集题目 有n个人 前m个人希望和第i个人坐在一起。桌子为圆桌 1号位置固定 输出所有的安排种数 对1e9+7取模
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int Mod=1e9+7;
long long Pow[110],A[110];//2的N次方和排列组合
int n,m;
int f[110],d[110],sum[110];//并查集祖父节点 每个点的度 每棵树的节点数
bool b[110][110],flag; //标记两个点是否连接 标记是否有环 是否有点度数大于2
int find(int i) //查找祖父节点
{
if(f[i]==i) return i;
else return find(f[i]);
}
int main()
{
Pow[0]=1;//预处理
A[0]=1;
for(int i=1; i<=100; i++)
Pow[i]=((Pow[i-1]*2)%Mod+Mod)%Mod;
for(int i=1; i<=100; i++)
A[i]=((A[i-1]*i)%Mod+Mod)%Mod;
while(~scanf("%d%d",&n,&m))
{
flag=0;
memset(d,0,sizeof(d));
memset(b,false,sizeof(b));
for(int i=1; i<=n; i++)
f[i]=i,sum[i]=1;
for(int i=1; i<=m; i++)
{
int j,f1,f2;
scanf("%d",&j);
f1=find(i);f2=find(j);
if(f1==f2)//如果两个点是同一颗树上的点
{
if(b[i][j]||b[j][i])//若两个点已经连接 则不做处理
{
;
}
else 如果没有连接
{
if(j==f1&&sum[j]!=n)//若有环 且环的节点数不等于所有人 则不存在安排方式
flag=true;
else
{d[i]++;d[j]++;b[i][j]=b[j][i]=true;} //两个节点连接 两个点的度都加1 改变两点连接状态
}
}
else //如果两个点不是同一颗树上的点
{
d[i]++;d[j]++;b[i][j]=b[j][i]=true; //连接两个节点 两个节点的度都加1 改变两点连接状态
sum[f1]+=sum[f2];f[f2]=f1; //将f2树连接到f1上
}
}
int Sum=n,tree=0;//Sum记录单节点树的总数
for(int i=1;i<=n;i++)
{
if(d[i]>2) //如果度大于2 则没有安排座位的方式
{
flag=true;
}
if(f[i]==i&&d[i]>0) //如果是父亲节点 并且度不为0 则为一颗多节点的树
{
tree++;
Sum-=sum[i];
}
}
if(flag)
{
printf("0\n");
}
else
{
long long summ=A[Sum+tree-1];
summ=((summ*Pow[tree])%Mod+Mod)%Mod; //总数计算方法 所有父亲节点全排列 每个父亲节点有两中排列方法
printf("%lld\n",summ);
}
}
return 0;
}