Best Theorem

简述

Best Theorem 是用来统计有向图中欧拉回路数目的定理

主要内容

\(t_x\)表示以\(x\)为根的树形图数量(可以用Matrix Tree求解),\(G=\{V,E\}\),那么有

\[ec(G)=t_x\cdot\prod_{i\in V}(deg_i-1)! \]

\(deg_x\)表示\(x\)的度,如果图中有欧拉回路,那么存在\(deg_{(in)x}=deg_{(out)x}\),所以可以随便选一种来统计。

code

\(n\) 个房间,每个房间有若干把钥匙能够打开特定房间的门。
最初你在房间 \(1\)。每当你到达一个房间,你可以选择该房间的一把钥匙,前往该钥匙对应的房间,并将该钥匙丢到垃圾桶中。
你希望最终回到房间 \(1\),且垃圾桶中有所有的钥匙。
你需要求出方案数,答案对 \(10^6 + 3\) 取模。两组方案不同,当且仅当使用钥匙的顺序不同。
注意,每把钥匙都是不同的。

code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
#define int long long
using namespace std;
namespace Q{
    il int rd(){
        ri int x=0;ri bool f=0;ri char c=getchar();
        while(!isdigit(c)) f|=(c=='-'),c=getchar();
        while(isdigit(c))x=x*10+(c^48),c=getchar();
        return f?-x:x;
    }
    il void wt(int x){
        if(x<0) x=-x,putchar('-');
        if(x>=10) wt(x/10);
        return putchar(x%10+48),void();
    }
    il int qpow(int a,int b,int p=1e6+3){
        ri int as=1;
        while(b>0){
            if(b&1) as=as*a%p;
            a=a*a%p,b>>=1;
        }
        return as;
    }
    il int inv(int a,int p=1e6+3){
        return qpow(a,p-2,p);
    }
} using namespace Q;
cs int N=105,mod=1e6+3;
struct qwq{
    int a[N][N];
    il void clr(){
        memset(a,0,sizeof(a));
    }
    il int getdet(int n,int p=mod){
        ri int as=1,l,iv;
        for(ri int i=2;i<=n;++i){
            if(!a[i][i]){
                for(ri int j=i+1;j<=n;++j){
                    if(a[j][i]){
                        swap(a[i],a[j]);
                        as*=-1;
                        break;
                    }
                }
            }
            if(!a[i][i]) continue;
            as=as*a[i][i]%p;
            iv=inv(a[i][i]);
            for(ri int j=i+1;j<=n;++j){
                l=a[j][i]*iv%mod;
                if(!l) continue;
                for(ri int k=i+1;k<=n;++k){
                    a[j][k]=(a[j][k]-a[i][k]*l%p+p)%p;
                }
            }
        }
        return as;
    }
}o;
int jc[1<<18|1];
il void init(int n){
    jc[0]=1;
    for(ri int i=1;i<=n;++i){
        jc[i]=jc[i-1]*i%mod;
    }
}
int t,n,in[N],out[N],f,T1,cnt;
int b[N];
il int up(int u){
    return u==b[u]?u:b[u]=up(b[u]);
}
signed main(){
    init(1<<18);
    t=rd();
    while(t--){
        memset(in,0,sizeof(in));
        cnt=0;
        o.clr();
        n=rd();
        for(ri int i=1;i<=n;++i) b[i]=i;
        for(ri int i=1,v;i<=n;++i){
            out[i]=rd();
            for(ri int j=1;j<=out[i];++j){
                v=rd(),o.a[v][v]++,o.a[i][v]--,in[v]++;
                if(o.a[v][v]>=mod) o.a[v][v]-=mod;
                if(o.a[i][v]<0) o.a[i][v]+=mod;
                b[up(i)]=up(v);
            }
        }
        f=0;
        for(ri int i=1;i<=n;++i){
            if(in[i]!=out[i]){f=1;break;}
        }
        if(!f) for(ri int i=1;i<=n;++i){
            if(up(i)!=up(1)&&in[i]){f=1;break;}
        }        
        if(f) { puts("0"); continue; } 
        T1=o.getdet(n)*jc[in[1]]%mod;
        //因为起点为 1,所以以 1 为开头的路,是可以循环同构的,而其他的点是不能循环同构
        //本质上,原来的best已经把从所有点出发的循环同构算过一遍了,唯独不算从根开始的全局构。
        //但现在要求算上,于是最后的结果需要乘上 deg1
        for(ri int i=2;i<=n;++i){
            if(!in[i]) continue;
            T1=T1*jc[in[i]-1]%mod;
            if(up(i)==up(1)) f=1;
        }
        if(f) wt(T1),putchar('\n');
        else wt(jc[in[1]]),putchar('\n');
        /*
        关于 #11 (没错,说的就是我)
        大概有这么几个情况吧
        -整张图没有边 ans=1 好奇这种情况为什么不是 0
        -有没有出入度的无效点 应该忽略掉
        -整张图不连通 ans=0
        -不存在欧拉路 ans=0
        -整张图存在边的情况下一号点的度数为0
        */
    }
    return 0;
}

edit

posted @ 2023-01-28 21:04  雨夜风月  阅读(15)  评论(0编辑  收藏  举报