【BZOJ 3659】 3659: Which Dreamed It (Matrix-Tree&BEST theorem )

3659: Which Dreamed It

Time Limit: 20 Sec  Memory Limit: 1024 MB
Submit: 134  Solved: 41

Description

有n个房间,每个房间有若干把钥匙能够打开特定房间的门。
你会做这么件事情:
最初你在房间1。
每当你到达一个房间,你可以选择该房间的一把钥匙,前往该钥匙对
应的房间,并将该钥匙丢到垃圾桶中。
你希望:最终回到房间1,且垃圾桶中有所有的钥匙。
求方案数。两组方案不同,当且仅当使用钥匙的顺序不同。注意,每
把钥匙都是不同的。

Input

 有多组数据。
对于每组数据第一行输入一个数n,表示房间数。
接下来n行依次描述每个房间:
首先一个数s,表示这个房间的钥匙数目,接下来s个数,分别描述每把
钥匙能够打开的房间的门。
输入以n-0结尾。

Output

对于每组数据,输出方案数,为了方便你的输出,请将答案对1000003取模。

Sample Input

1
0
2
1 1
1 2
0

Sample Output

1
0

HINT

在第一组样例中,没有钥匙,则方案数为1。

在第二组样例中,你不可能使用第二个房间的钥匙,所以方案数为0。







房间数小于等于100,钥匙数小于等于200000。

数据组数也不是特别多。

Source

 

 

【分析】

  这种题叫做结论题。

  %CA爷

  然后这里有两个结论 
  1.有向图以i为根的树形图的数目=基尔霍夫矩阵去掉第i行和第i列的主子式的行列式的值(即Matrix-Tree定理不仅适用于求无向图生成树数目,也适用于求有向图树形图数目) 
  2.以某个点为起点的欧拉回路数=该点为根的树形图数*(所有点出度-1)的乘积(本名BEST theorem) 
  关于BEST theorem可以提供一个wiki上的讲解:about BEST theorem 

 

  最后还有乘上起点的出度?【怎么没说。。

 

【我觉得我Mod那里应该有问题,但是我没有被卡哦。。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define LL long long
 8 const int Mod=1000003;
 9 #define Maxn 1000010
10 
11 int a[110][110],m[110],fac[Maxn];
12 
13 int qpow(int x,int b)
14 {
15     x%=Mod;
16     int ans=1;
17     while(b)
18     {
19         if(b&1) ans=1LL*ans*x%Mod;
20         x=1LL*x*x%Mod;
21         b>>=1;
22     }
23     return ans;
24 }
25 
26 int gauss(int n)
27 {
28     if (n==0) return 1;
29     int ans=1;
30     for(int i=1;i<=n;i++)
31     {
32         int t=i;
33         for(int j=i+1;j<=n;j++) if(a[j][i]>a[t][i]) t=j;
34         if(a[t][i]==0) return 0;
35         if(t!=i)
36         {
37             ans=-ans;
38             for(int j=1;j<=n;j++) swap(a[t][j],a[i][j]);
39         }
40         int ny=qpow(a[i][i],Mod-2);
41         for(int j=i+1;j<=n;j++)
42         {
43             int nw=1LL*ny*a[j][i]%Mod;
44             for(int k=i;k<=n;k++) a[j][k]-=1LL*a[i][k]*nw%Mod,a[j][k]=(a[j][k]%Mod+Mod)%Mod;
45         }
46     }
47     for(int i=1;i<=n;i++) ans=1LL*ans*a[i][i]%Mod;
48     ans=(ans%Mod+Mod)%Mod;
49     return ans;
50 }
51 
52 int main()
53 {
54     int n;
55     fac[0]=1;for(int i=1;i<=Mod;i++) fac[i]=1LL*fac[i-1]*i%Mod;
56     while(1)
57     {
58         scanf("%d",&n);
59         if(!n) break;
60         memset(a,0,sizeof(a));
61         for(int i=1;i<=n;i++)
62         {
63             scanf("%d",&m[i]);
64             for(int j=1;j<=m[i];j++)
65             {
66                 int x;
67                 scanf("%d",&x);
68                 if(i!=x) a[i][x]--,a[i][i]++;
69             }
70         }
71         if(n==1&&!m[1]) {printf("1\n");continue;}
72         int ans=1;
73         for(int i=1;i<=n;i++) ans=1LL*ans*fac[m[i]-1]%Mod;
74         ans=1LL*ans*m[1]%Mod;
75         ans=1LL*ans*gauss(n-1)%Mod;
76         printf("%d\n",ans);
77     }
78     return 0;
79 }
View Code

 

2017-04-16 20:11:07

转载于:https://www.cnblogs.com/Konjakmoyu/p/6719773.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值