bzoj3659 Which dreamed it
题目描述:
有n个房间,每个房间有若干把钥匙能够打开特定房间的门。
你会做这么件事情:
最初你在房间1。
每当你到达一个房间,你可以选择该房间的一把钥匙,前往该钥匙对应的房间,并将该钥匙丢到垃圾桶中。
你希望:最终回到房间1,且垃圾桶中有所有的钥匙。
求方案数。两组方案不同,当且仅当使用钥匙的顺序不同。注意,每把钥匙都是不同的。
题解:
有个东西叫$BEST$定理。意思是一个有向图的欧拉回路数为生成数个数*$\prod^{n}_{i=1}(deg[i]-1)!$。
里面那个$deg$是入度或出度,反正一定相等。
由于本题要求边序不同即可,所以答案要多乘一个$deg[1]$。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N = 105; const int MOD = 1000003; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x = f*c; } int n,ind[N],otd[N],a[N][N]; bool check() { for(int i=1;i<=n;i++) if(ind[i]!=otd[i])return 0; return 1; } ll fastpow(ll x,int y) { ll ret = 1ll; while(y) { if(y&1)ret=ret*x%MOD; x=x*x%MOD; y>>=1; } return ret; } ll ma_tr() { for(int i=1;i<n;i++) for(int j=1;j<n;j++) a[i][j]=(a[i][j]%MOD+MOD)%MOD; ll ret = ind[1]; /* for(int i=1;i<n;i++) for(int j=i+1;j<n;j++) while(a[j][i]) { ll d = a[i][i]/a[j][i]; if(d)for(int k=i;k<n;k++) a[i][k]=(a[i][k]-d*a[j][k]%MOD+MOD)%MOD; swap(a[i],a[j]);ret=-ret; }*/ for(int i=1;i<n;i++) for(int j=i+1;j<n;j++) while(a[j][i]) { ll t = a[i][i]/a[j][i]; if(t)for(int k=i;k<n;k++) a[i][k]=(a[i][k]-t*a[j][k]%MOD+MOD)%MOD; swap(a[i],a[j]);ret=-ret; } for(int i=1;i<n;i++) ret=(ret*a[i][i]%MOD+MOD)%MOD; return ret; } int main() { while(1) { read(n); if(!n)break; memset(a,0,sizeof(a)); memset(ind,0,sizeof(ind)); memset(otd,0,sizeof(otd)); for(int i=1;i<=n;i++) { read(otd[i]);a[i][i]=otd[i]; for(int j=1,x;j<=otd[i];j++) { read(x); ind[x]++; a[i][x]--; } } if(!check()){puts("0");continue;} ll ans = ma_tr(); for(int i=1;i<=n;i++) for(int j=1;j<ind[i];j++) ans=ans*j%MOD; printf("%lld\n",ans%MOD); } return 0; }