题目描述
[题目链接](http://acm.hdu.edu.cn/showproblem.php?pid=6481)
求把 $1 \sim 2n$ 分成 $n$ 个集合的方案数,要求每个集合大小恰好为 $2$,答案模 $2^{64}$
数据范围:$1 \le n \le 10^{18}$
题解
首先枚举 $2n$ 的全排列,那么相邻两个配对一下就是答案了,但这样每种配对方式会被算两次,因此答案为:
$$
\frac{(2n)!}{n!2^{n}}
$$
考虑到 $(2n)!=1 \times 2 \times 3 \times \cdots \times n \times (n+1) \times (n+2) \times \cdots \times (2n)$
对于所有偶数,都提出一个 $2$,则 $(2n)!=2^n \cdot n! \cdot \prod_{i=1}^{n}(2i-1)$
即 $\frac{(2n)!}{n!2^{n}}=\prod_{i=1}^{n}(2i-1)$
考虑到模 $2^{64}$,也就是 $(2x)^t$ 到达一定程度后就成了 $0$
因此可以构造多项式 $F_n(x)=\prod_{i=1}^{n}(2i-1+2x)$,答案为 $F(0)$,同时它的次数小于 $64$
考虑倍增,假设现在知道了 $F_n(x)$,目标要求 $F_{2n}(x)$,即:
$$
\begin{aligned}
F_{2n}(x)=F_n(x) \cdot F_{n}(n+x)
\end{aligned}
$$
由于已经知道了 $F_n(x)=\sum_{i=0}^{63}f_ix^i$,因此:
$$
\begin{aligned}
F_{n}(n+x)
=&\sum_{i=0}^{63}f_i (x+n)^i \\
=&\sum_{i=0}^{63} f_i \sum_{j=0}^{i} {i \choose j} n^{i-j}x^j \\
=&\sum_{j=0}^{63}x^j \sum_{i=j}^{63} {i \choose j} n^{i-j} f_i
\end{aligned}
$$
然后就多项式快速幂就好了
题解
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef unsigned long long ull; 4 5 ull C[110][110]; 6 7 struct POLY { 8 ull f[64]; // x^0 ~ x^63 9 POLY() { memset(f, 0, sizeof f); } 10 ull &operator [] (int x) { 11 return f[x]; 12 } 13 POLY operator * (POLY g) { 14 POLY res; 15 for(int i = 0 ; i < 64 ; ++ i) { 16 for(int j = 0 ; i + j < 64 ; ++ j) { 17 res[i + j] += f[i] * g[j]; 18 } 19 } 20 return res; 21 } 22 POLY getnew(ull n) { 23 POLY res; 24 for(int j = 0 ; j < 64 ; ++ j) { 25 ull fac = 1; 26 for(int i = j ; i < 64 ; ++ i) { 27 res[j] += C[i][j] * fac * f[i]; 28 fac *= n; 29 } 30 } 31 return res; 32 } 33 }; 34 35 POLY sol(ull n) { 36 if(n == 0) { 37 POLY res; 38 res[0] = 1; 39 return res; 40 } else if(n & 1) { 41 POLY lef = sol(n - 1); 42 POLY rig; 43 rig[0] = 2 * n - 1, rig[1] = 2; 44 return lef * rig; 45 } else { 46 POLY lef = sol(n / 2); 47 POLY rig = lef.getnew(n / 2); 48 return lef * rig; 49 } 50 } 51 52 void runprog() { 53 ull n; 54 cin >> n; 55 POLY res = sol(n); 56 cout << res[0] << endl; 57 } 58 59 int main() { 60 C[0][0] = 1; 61 for(int i = 1 ; i <= 100 ; ++ i) { 62 C[i][0] = 1; 63 for(int j = 1 ; j <= 100 ; ++ j) { 64 C[i][j] = C[i - 1][j - 1] + C[i - 1][j]; 65 } 66 } 67 ios :: sync_with_stdio(0); 68 int t; cin >> t; 69 while(t --) { 70 runprog(); 71 } 72 }