依然是liurujia计数练习题。依然是自己想没想出来,在MOD是素数的情况下除以x即为乘x的逆。这个真心以前没听过,用了这个方法后处理就变得十分巧妙。
整个程序步骤还是很清晰的,先上来算阶乘与逆(求数的逆还是有点没理解透,需要后续章节继续学习)。然后读入建图只能用邻接表了,注意加上最开始的那个根节点就行了。
然后就是搜索按照书上的公式计算就行了。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 #include <cmath> 6 #include <algorithm> 7 #define LEN 40100 8 #define MOD 1000000007 9 #define ll long long 10 using namespace std; 11 12 int n, m, isrt[LEN]; 13 ll jc[LEN], rjc[LEN], ans; 14 vector<ll> map[LEN]; 15 16 //扩展欧几里德 17 ll extend_gcd(ll a, ll b, ll &x,ll &y){ 18 ll t,_m; 19 if ((b==0)&(a==0)) return -1;//表示无最大公因数 20 if (b==0){ 21 x=1;y=0; return a; 22 } 23 else { 24 _m=extend_gcd(b,a % b,x,y); 25 t=x;x=y;y=t-(a/b)*y; 26 } 27 return _m; 28 } 29 30 //初始化阶乘和他的逆 31 void cntjc() 32 { 33 ll x,y; 34 jc[0] = 1; 35 for(int i=1; i<LEN; i++){ 36 jc[i] = (jc[i-1]*i)%MOD; 37 ll ta = extend_gcd(i,MOD,x,y); 38 if(ta==1 || ta==-1) rjc[i] = (x+MOD)%MOD; 39 else rjc[i] = -1; 40 } 41 } 42 43 void init() 44 { 45 memset(isrt, 0, sizeof isrt); 46 for(int i=0; i<LEN; i++){ 47 map[i].clear(); 48 } 49 //建图 50 int a, b; 51 scanf("%d%d", &n, &m); 52 for(int i=0; i<m; i++){ 53 scanf("%d%d", &a, &b); 54 map[b].push_back(a); 55 isrt[a] = 1; 56 } 57 //加入总的根节点 58 for(int i=1; i<=n; i++){ 59 if(!isrt[i]) map[0].push_back(i); 60 } 61 } 62 63 int DFS(int vex) 64 { 65 int cnt = 1; 66 for(vector<ll>::iterator it=map[vex].begin(); it!=map[vex].end(); ++it){ 67 cnt+=DFS(*it); 68 } 69 if(vex) ans = ans*rjc[cnt]%MOD; 70 return cnt; 71 } 72 73 int main() 74 { 75 // freopen("in.txt", "r", stdin); 76 77 int T; 78 scanf("%d", &T); 79 cntjc(); 80 while(T--) 81 { 82 init(); 83 ans = jc[n]; 84 DFS(0); 85 printf("%lld ", ans); 86 } 87 return 0; 88 }