BZOJ 3659: Which Dreamed It 基尔霍夫矩阵 best theorem

3659: Which Dreamed It

Time Limit: 20 Sec  Memory Limit: 1024 MB
Submit: 172  Solved: 49
[Submit][Status][Discuss]

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。
数据组数也不是特别多。


定理题 这定理命名就是最大的槽点

Best Theorem

以某个点为起点的欧拉回路数=该点为根的树形图数*((每个点出度-1 )的阶乘)
这个题再多乘一个d[1]因为不同点出发算不同方案

基尔霍夫矩阵适用于有向图 度数矩阵存出度 邻接矩阵正常搞

重边不怕照样好使 自环删掉 找搞不误

这是今天最后一道矩阵树定理了 吧。。。。

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>

using namespace std;

typedef long long ll;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=110,mod=1000003;

int a[N][N],d[N],fac[mod];

void initial()
{memset(a,0,sizeof(a));memset(d,0,sizeof(d));}

int det(int n)
{
	register int i,j,k,res=1,x,y,tmp,f=1;
	for(i=1;i<=n;++i)for(j=1;j<=n;++j)a[i][j]=(a[i][j]+mod)%mod;
	for(i=1;i<=n;++i)
	{
		for(j=i+1;j<=n;++j)
		{
			x=a[i][i],y=a[j][i];
			while(y)
			{
				tmp=x/y;x%=y;swap(x,y);
				for(k=i;k<=n;++k)
				a[i][k]=((a[i][k]-1ll*tmp*a[j][k])%mod+mod)%mod;
				for(k=i;k<=n;++k)swap(a[i][k],a[j][k]);
				f=-f;
			}
		}
		if(!a[i][i])return 0;
		res=1ll*res*a[i][i]%mod;
	}
	if(f==-1)res=mod-res;
	return (res%mod+mod)%mod;
}

int main()
{
	
	register int i,j,v,n=read(),ans;
	fac[0]=1;for(i=1;i<mod;++i)fac[i]=1ll*fac[i-1]*i%mod;//cout<<n<<endl;
	while(n)
	{
		ans=1;
		for(i=1;i<=n;++i)
		{
			d[i]=read();
			for(j=1;j<=d[i];++j){v=read();if(i!=v)a[i][i]++,a[i][v]--;}
		}
		if(n==1&&!d[1]){puts("1");n=read();continue;}
		for(i=1;i<=n;++i)ans=1ll*ans*fac[d[i]-1]%mod;
		ans=1ll*ans*det(n-1)%mod;ans=1ll*ans*d[1]%mod;
		print(ans);puts("");
		n=read();initial();
	}
	return 0;
}
/*
1
0
2
1 1
1 2
0

1
0
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值