1 条题解
-
0
思路与算法
按输入顺序先读 、再读 ,用二进制快速幂求 。所有 都要求为正,先令 ,则 。插板法得到有序正整数组数
保证 ,且 、。答案没有再取模的要求,可能远大于64位。程序以 为一段存储大整数,手写逐段相加与进位。用一维Pascal递推求组合数:初始 ,逐行更新,列下标从大到小执行 ,最后输出 。
正确性说明
从 个单位中挑选 个内部切口,得到 个非空连续段,其段长恰为一个有序正整数解;任一正整数解也唯一确定这些切口。因此解数为 。
Pascal恒等式 分类考虑一个指定元素是否入选。降序更新保证右侧两项仍来自上一行,归纳得到第 行目标组合数。逐段加法按基数进位保存精确整数,不发生模截断;快速幂精确得到 ,所以打印结果正确。
复杂度
设大整数最多占 个基数 的段。模幂为 ,初始化 个数为 ,Pascal更新至多 次,每次加法 ,打印 。总时间 ,额外空间 ;没有保存整个二维Pascal表。
易错点
- 输入是 ,方程右边为 的模1000余数,不是 。
- 变量要求正整数,组合数是 ;直接按非负变量计数会多算。
- 答案精确输出,不对1000取模,不用浮点或64位代替高精度。
- Pascal列下标必须降序;最高段正常打印,其余段补足9位。给定范围包含 ,此时唯一解为 。
- 覆盖超过64位的 、 以及最大 。独立答案使用任意精度精确组合数,并以小范围正整数组直接枚举核对。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; using ll = long long; struct Big { static const unsigned BASE = 1000000000; vector<unsigned> d; Big(unsigned v = 0) : d(1, v) { } void add(const Big &b) { size_t size = max(d.size(), b.d.size()); d.resize(size); ll carry = 0; for (size_t i = 0; i < size; i++) { ll sum = (ll)d[i] + (i < b.d.size() ? b.d[i] : 0) + carry; d[i] = sum % BASE; carry = sum / BASE; } if (carry) d.push_back(carry); } void print() const { cout << d.back(); for (int i = (int)d.size() - 2; i >= 0; i--) cout << setw(9) << setfill('0') << d[i]; cout << '\n'; } }; ll power(ll a, ll e) { a %= 1000; ll result = 1; while (e) { if (e & 1) result = result * a % 1000; a = a * a % 1000; e >>= 1; } return result; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int k; ll x; cin >> k >> x; int g = power(x, x); vector<Big> dp(k); dp[0] = Big(1); for (int row = 1; row <= g - 1; row++) for (int j = min(row, k - 1); j >= 1; j--) dp[j].add(dp[j - 1]); dp[k - 1].print(); }
- 1