這個Best定理的名字似乎是四個人的姓的首字母拼起來的
我還以爲是最好的定理的意思
前置技能:矩陣樹定理(Matrix-tree)
無向圖的生成樹個數即基爾霍夫矩陣的行列式
有向圖的生成樹有兩種:外向的和內向的
外向樹即所有邊從根節點指出去
內向樹即所有邊指向根節點
外向生成樹的基爾霍夫矩陣爲入度矩陣-度數矩陣
內向生成樹的基爾霍夫矩陣爲出度矩陣-度數矩陣
有向生成樹個數爲其基爾霍夫矩陣除了根節點的那行的主子式之和
Best定理:一個點出發的歐拉回路個數爲
即以這個點爲根的外向生成樹個數乘上每個點的度數-1的階乘(其實以任意點爲根都行)
證明感性理解下吧我不會qwq
然後如果經過的邊的序列不同算不同的方案的話還要乘上一個根節點的度數
Code:
#include<bits/stdc++.h>
#define mod 1000003
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline void Mul(int &x,int y){x=1ll*x*y%mod;}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a)) if(b&1) res=mul(res,a);return res;}
const int N=105,M=2e5+5;
int e[N][N],a[N][N];
int fac[M],ifac[M];
inline void init(int n){
fac[0]=ifac[0]=1;
for(int i=1;i<=n;i++) fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
for(int i=n-1;i;i--) ifac[i]=mul(ifac[i+1],i+1);
}
inline int det(int n){
int ans=1;
for(int i=1;i<=n;i++){
int pos;for(pos=i;pos<=n;pos++) if(a[pos][i]) break;
if(pos==n+1) return 0;
if(pos!=i){swap(a[i],a[pos]);ans=dec(0,ans);}
Mul(ans,a[i][i]);
int iv=ksm(a[i][i],mod-2);
for(int j=i;j<=n;j++) Mul(a[i][j],iv);
for(int j=i+1;j<=n;j++) if(a[j][i]){
iv=a[j][i];
for(int k=i;k<=n;k++) Dec(a[j][k],mul(iv,a[i][k]));
}
}
return ans;
}
int ou[N];
int main(){
init(200000);
while(1){
memset(a,0,sizeof(a));
int n=read();
if(n==0) return 0;
for(int i=1;i<=n;i++){
int k=read();ou[i]=k;
for(int j=1;j<=k;j++){
int x=read();
if(x^i) --a[i][x],++a[i][i];
}
}
if(n==1){cout<<fac[ou[1]]<<"\n";continue;}
int ans=det(n-1);
for(int i=1;i<=n;i++) Mul(ans,fac[ou[i]-1]);
cout<<mul(ans,ou[1])<<"\n";
}
}