链接:https://www.nowcoder.com/acm/contest/147/D
来源:牛客网
Niuniu likes traveling. Now he will travel on a special graph.
Given k and n, The directed graph contains n vertices, which are numbered from 0 to n - 1. For the vertex i, and for 1 <= j <= k, there is a directed edge from vertex i to vertex ((i + j) % n).
We want to know the number of (directed) cycles, that pass each directed edge exactly once.
As the answer might be very large, you only need to output the answer mod 1000000007.
As the answer might be very large, you only need to output the answer mod 1000000007.
输入描述:
The first and only line contains two integers, which are k and n.
1 <= k <= 7
2k+1 <= n <= 109
输出描述:
The first and only line contains the answer.
示例1
输入
2 5
输出
11
说明
The answer is not 22.
0 -> 1- > 2 -> 3 -> 4 -> 0 -> 2 -> 4 -> 1 -> 3 -> 0.
0 -> 2 -> 4 -> 1 -> 3 -> 0 -> 1 -> 2 -> 3 -> 4 -> 0.
The two cycles are the same. They all passed the 10 edges.
Only the start edges are different, and we think they are the same.
输入
3 8
输出
278528
解析 按照以上方式建立有向图 求欧拉回路的个数 。首先我们知道BEST定理 用矩阵求解欧拉回路的个数。但是题目给的点数太多,不能直接暴力
每个点的出度和入度都为K 感觉应该是有什么规律,打个表怀疑可能是线性递推,试着下一下就是了。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define rep(i,a,n) for (int i=a;i<n;i++) 4 #define per(i,a,n) for (int i=n-1;i>=a;i--) 5 #define pb push_back 6 #define mp make_pair 7 #define all(x) (x).begin(),(x).end() 8 #define fi first 9 #define se second 10 #define SZ(x) ((int)(x).size()) 11 typedef vector<int> VI; 12 typedef long long ll; 13 typedef pair<int,int> PII; 14 const ll mod=1000000007; 15 ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} 16 // head 17 18 namespace linear_seq { 19 const int N=10010; 20 ll res[N],base[N],_c[N],_md[N]; 21 vector<int> Md; 22 void mul(ll *a,ll *b,int k) { 23 rep(i,0,k+k) _c[i]=0; 24 rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod; 25 for (int i=k+k-1;i>=k;i--) if (_c[i]) 26 rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod; 27 rep(i,0,k) a[i]=_c[i]; 28 } 29 int solve(ll n,VI a,VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+... 30 // printf("SIZE %d ",SZ(b)); 31 ll ans=0,pnt=0; 32 int k=SZ(a); 33 assert(SZ(a)==SZ(b)); 34 rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1; 35 Md.clear(); 36 rep(i,0,k) if (_md[i]!=0) Md.push_back(i); 37 rep(i,0,k) res[i]=base[i]=0; 38 res[0]=1; 39 while ((1ll<<pnt)<=n) pnt++; 40 for (int p=pnt;p>=0;p--) { 41 mul(res,res,k); 42 if ((n>>p)&1) { 43 for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0; 44 rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod; 45 } 46 } 47 rep(i,0,k) ans=(ans+res[i]*b[i])%mod; 48 if (ans<0) ans+=mod; 49 return ans; 50 } 51 VI BM(VI s) { 52 VI C(1,1),B(1,1); 53 int L=0,m=1,b=1; 54 rep(n,0,SZ(s)) { 55 ll d=0; 56 rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod; 57 if (d==0) ++m; 58 else if (2*L<=n) { 59 VI T=C; 60 ll c=mod-d*powmod(b,mod-2)%mod; 61 while (SZ(C)<SZ(B)+m) C.pb(0); 62 rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod; 63 L=n+1-L; B=T; b=d; m=1; 64 } else { 65 ll c=mod-d*powmod(b,mod-2)%mod; 66 while (SZ(C)<SZ(B)+m) C.pb(0); 67 rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod; 68 ++m; 69 } 70 } 71 return C; 72 } 73 int gao(VI a,ll n) { 74 VI c=BM(a); 75 c.erase(c.begin()); 76 rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod; 77 return solve(n,c,VI(a.begin(),a.begin()+SZ(c))); 78 } 79 }; 80 int a[2020][2020]; 81 ll det(int n) { 82 ll ans = 1; 83 for (int i = 0; i < n; i++) { 84 for (int j = i + 1; j < n; j++) { 85 while (a[j][i] != 0) { 86 int u = a[i][i] / a[j][i]; 87 for (int k = 0; k < n; k++) { 88 int t = (a[i][k] - (ll)a[j][k] * u % mod + mod) % mod; 89 a[i][k] = a[j][k]; 90 a[j][k] = t; 91 } 92 ans = -ans; 93 } 94 } 95 ans = ans * a[i][i] % mod; 96 } 97 if (ans < 0) { 98 ans += mod; 99 } 100 return ans; 101 } 102 ll work(int k, int n) { //构造矩阵 计算点数为n的欧拉回路个数 103 memset(a, 0, sizeof a); 104 for (int i = 0; i < n; i++) { 105 a[i][i] = k; 106 for (int j = 1; j <= k; j++) { 107 a[i][(i + j) % n] = -1; 108 } 109 } 110 ll t = 1; 111 for (int i = 1; i < k; i++) { //度数-1的阶乘 112 t = t * i % mod; 113 } 114 return (ll)det(n - 1) * powmod(t, n) % mod; // 每个点的度数都一样 所以直接快速幂。 115 } 116 int main() { 117 int k; 118 ll n; 119 cin >> k >> n; 120 vector<int> a; 121 for (int i = 2 * k + 1; i <= (1 << k)+ 2 * k + 1; i++) { //为什么是1<<k这么多项 也也不知道 题解说的。。。 正常写就在不超时的情况下尽量写大点呗 122 a.push_back(work(k, i)); 123 } 124 cout << linear_seq::gao(a, n - (2 * k + 1)) << endl; 125 return 0; 126 }