In Chinese Restaurant

In Chinese Restaurant

1000ms
65536KB
This problem will be judged on Ural. Original ID:  1962
64-bit integer IO format:  %lld      Java class name:  (Any)
When Vova arrived in Guangzhou, his Chinese friends immediately invited him to a restaurant. Overall  n people came to the restaurant, including Vova. The waiter offered to seat the whole company at a traditional large round table with a rotating dish plate in the center.
As Vova was a guest, he got the honorable place by the door. Then  m people in the company stated that they wanted to sit near a certain person. Your task is to determine the number of available arrangements to seat Vova's friends at the table.

Input

The first line contains integers  n and  m (2 ≤  n ≤ 100; 0 ≤  m ≤  n). The next  m lines contain integers  k 1, …,  k m, where  k i is the number of the person who the person number  i wants to sit with (1 ≤  k i ≤  nk i ≠  i). Being an honorable guest, Vova got number 1. His friends are numbered with integers from 2 to  n.

Output

Print the number of available arrangements to seat Vova's friends modulo 10 9 + 7.

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值