【2018 黑龙江省大学生程序设计竞赛】A Math Problem

题目描述

[题目链接](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 }
【2018 黑龙江省大学生程序设计竞赛】A Math Problem

 

posted @ 2019-06-14 14:45  KingSann  阅读(383)  评论(0编辑  收藏  举报