【BZOJ3659】Which Dreamed It(BEST定理)
大致题意: 求有向图欧拉回路个数。
\(BEST\)定理
有向图欧拉回路个数为:
\[Tree(x)\times \sum_{i=1}^n(deg_i-1)!
\]
其中\(Tree(x)\)为以\(x\)为根的外向树个数,\(deg_i\)为每个点的入度。
\(Tree(x)\)可以用矩阵树定理求出,具体可见此题:【BZOJ4894】天赋(外向树的矩阵树定理)。
至于这个的意义,大致就是在确定一棵生成树的基础上,枚举每条边走的顺序,必然对应了一种方案。
不过由于此题中起点从不同边出发有不同走法,而这些走法会被视为同一种欧拉回路,因此还需要将答案乘上\(deg_1\)。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
#define S 200000
#define X 1000003
#define Inc(x) (++x==X&&(x=0))
#define Dec(x) (!x--&&(x=X-1))
using namespace std;
int n,Fac[S+5],d[N+5],op,a[N+5][N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
I void Find(CI x)
{
if(a[x][x]) return;RI i;for(i=x+1;i<=n&&!a[i][x];++i);
for(RI j=x;j<=n;++j) swap(a[x][j],a[i][j]);op=X-op;
}
int main()
{
RI i,j,k;for(Fac[0]=i=1;i<=S;++i) Fac[i]=1LL*Fac[i-1]*i%X;//预处理阶乘
RI Tt,x,t;W(scanf("%d",&n),n)
{
for(i=1;i<=n;++i) for(j=1;j<=n;++j) a[i][j]=0;//清空
for(i=1;i<=n;++i) for(scanf("%d",&d[i]),j=1;j<=d[i];++j) scanf("%d",&x),Inc(a[x][x]),Dec(a[i][x]);//入度矩阵-邻接矩阵
if(n==1) {printf("%d\n",Fac[d[1]]);continue;}//特判n=1
for(op=1,i=2;i<=n;++i) for(Find(i),j=i+1;j<=n;++j)//高斯消元求行列式
for(t=X-1LL*a[j][i]*QP(a[i][i],X-2)%X,k=i;k<=n;++k) a[j][k]=(1LL*t*a[i][k]+a[j][k])%X;
for(t=op,i=2;i<=n;++i) t=1LL*t*a[i][i]%X;for(i=1;i<=n;++i) t=1LL*t*Fac[d[i]-1]%X;//BEST定理
printf("%d\n",1LL*t*d[1]%X);//最后乘上deg[1]
}return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒