154 lines
5.4 KiB
Markdown
154 lines
5.4 KiB
Markdown
##[$AcWing$ $1308$. 方程的解](https://www.acwing.com/problem/content/1310/)
|
||
|
||
### 一、题目描述
|
||
佳佳碰到了一个难题,请你来帮忙解决。
|
||
|
||
对于不定方程 $a_1+a_2+⋯+a_{k−1}+a_k=g(x)$,其中 $k≥1$ 且 $k∈N∗$,$x$ 是正整数,$g(x)=x^x \ mod \ 1000$(即 $x^x$ 除以 $1000$ 的余数),$x,k$ 是给定的数。
|
||
|
||
我们要求的是这个不定方程的 **正整数解** 组数。
|
||
|
||
举例来说,当 $k=3,x=2$ 时,方程的解分别为:
|
||
$$
|
||
\large \left\{\begin{matrix}
|
||
a_1=1\\
|
||
a_2=1 \\
|
||
a_3=2
|
||
\end{matrix}\right.
|
||
\left\{\begin{matrix}
|
||
a_1=1\\
|
||
a_2=2 \\
|
||
a_3=1
|
||
\end{matrix}\right.
|
||
\left\{\begin{matrix}
|
||
a_1=2\\
|
||
a_2=1 \\
|
||
a_3=1
|
||
\end{matrix}\right.
|
||
$$
|
||
|
||
|
||
**输入格式**
|
||
有且只有一行,为用空格隔开的两个正整数,依次为 $k,x$。
|
||
|
||
**输出格式**
|
||
有且只有一行,为方程的正整数解组数。
|
||
|
||
**数据范围**
|
||
$1≤k≤100,1≤x<2^{31},k≤g(x)$
|
||
|
||
**输入样例**:
|
||
```cpp {.line-numbers}
|
||
3 2
|
||
```
|
||
|
||
**输出样例**:
|
||
```cpp {.line-numbers}
|
||
3
|
||
```
|
||
|
||
### 二、隔板法求组合数问题
|
||
|
||
考虑问题:$a_1+a_2+…+a_k=n$,要求$a_i(i\in [1,k])$ **都是正整数** , 共有多少种$\{a_1,a_2,…,a_k\}$ 的组合能够满足上述等式。
|
||
|
||
#### 隔板法
|
||
- 构造$n$个小球`* * * * * * *`,则$n$个小球共有$n−1$个空隙
|
||
|
||
- 每插入一个空隙,从该空隙向左直到遇到第一个板子或左边界为止的这个区间的和,记为该数$a_i$的值
|
||
|
||
- 为实现把原区间分成 $k$ 个子区间目标,共需插入$k−1$个板子
|
||
|
||
**举栗子**
|
||
$n=7,k=4$时`* *|* *|* *|*`是一种方案,对应了 $\displaystyle
|
||
\left\{\begin{matrix}
|
||
a_1=2 \\
|
||
a_2=2 \\
|
||
a_3=2 \\
|
||
a_4=1
|
||
\end{matrix}\right.
|
||
$
|
||
|
||
方案总数=$\large \displaystyle C_{n-1}^{k-1}$
|
||
|
||
**代码思路**:
|
||
- 快速幂求$n=x^x$
|
||
- 隔板法求组合数$C_{n−1}^{k−1}$
|
||
|
||
**本题答案没有要求取模**,答案上界的极限是$C_{1000-1}^{100-1}$
|
||
根据组合数学公式 $\displaystyle C_n^m=\frac{n!}{m! \cdot (n-m)!}
|
||
=\frac{1000!}{100!\cdot 900!} \approx 10^{139} $
|
||
> <font color='red'><b>上面这个精度的估算,可以使用电脑自带的计算器,采用科学型就能计算阶乘了,记得算完第一个除法后按一下确定,$yxc$犯了这个错误~</b></font>
|
||
必爆$int$和$long$ $long$,$\_\_int128$!
|
||
|
||
<center><img src='https://cdn.acwing.com/media/article/image/2021/04/20/61813_9a2a4158a1-image-20210420154118483.png'></center>
|
||
|
||
需要 **高精度+递推**,可以视为一个 **模板**
|
||
|
||
|
||
### 三、实现代码
|
||
|
||
```cpp {.line-numbers}
|
||
#include <bits/stdc++.h>
|
||
using namespace std;
|
||
|
||
const int N = 150, mod = 1000;
|
||
|
||
int k, x;
|
||
int f[1010][110][N];
|
||
// 前两维:组合数C(n-1,k-1),其中n最大值是1000,k最大值是100,还都减了1, 所以这么开就足够大了,再+10肯定够了
|
||
// 第三维: 高精度的结果
|
||
|
||
// 快速幂
|
||
int qmi(int a, int b, int p) {
|
||
int res = 1;
|
||
while (b) {
|
||
if (b & 1) res = res * a % p;
|
||
a = a * a % p;
|
||
b >>= 1;
|
||
}
|
||
return res;
|
||
}
|
||
|
||
// 因为本题需要计算组合数,就是在(i,j)确定下的第三维数组空间保存结果
|
||
void add(int a[], int b[], int c[]) {
|
||
for (int i = 0, t = 0; i < N; i++) {
|
||
// 开的静态数组 N的长度足够(150是通过calc估算出的139上浮得到的极限值)
|
||
// 不用对比两个高精度的长度,全部当做150极限来看,也没多大
|
||
t += a[i] + b[i];
|
||
c[i] = t % 10;
|
||
t /= 10;
|
||
}
|
||
}
|
||
|
||
int main() {
|
||
cin >> k >> x; // k个数字, g(x)=x^x 指引我们使用快速幂
|
||
|
||
// 技巧:在求快速幂+模时,时刻注意取模
|
||
// 比如先把底、幂次取模后再计算快速幂
|
||
int n = qmi(x % mod, x % mod, mod);
|
||
|
||
// 递推求组合数,一直递推到C(n,k)
|
||
for (int i = 0; i <= n; i++)
|
||
for (int j = 0; j <= min(i, k); j++) // 孙猴子再厉害,也不能翻出如来佛的五指山,k<=i
|
||
if (!j)
|
||
f[i][j][0] = 1; // C(i,0)=1,所以C(i,0)指向的其实是一个一维数组,也就是C(i,0)的高精度结果。此结果=1.按高精度来说,就是a[0]=1
|
||
else
|
||
// C(i,j)=C(i-1,j)+C(i-1,j-1)
|
||
// 左侧:从i个小球中选择j个小球的方法数
|
||
// 右侧:(1) 不选第1个,那么在剩余i-1个小球中选择j个小球的方法数
|
||
// 右侧:(2) 选择第1个,那么在剩余i-1个小球中选择j-1个小球的方法数
|
||
// 再结合加法原理,即可得到上面的公式,这个公式可以用于递推
|
||
// 这个三维数组+高精度用的漂亮啊!
|
||
// f[][][]是三维数组,f[i - 1][j] 是指在i-1个中选择j个有多少种办法,其实结果是存在第三维中,
|
||
// f[i - 1][j]指向的是一维数组,也就是f[i-1][j]的方法数的高精度结果
|
||
// 理解为add(a[],b[],c[]);也就是简单的高精度加法
|
||
add(f[i - 1][j], f[i - 1][j - 1], f[i][j]);
|
||
|
||
// 上面多求了一些数据出来,我们只需要: C(n - 1, k - 1)
|
||
// 将前二维数组简写为g,此处g为一个指针,指向的是f[n-1][k-1]指向的高精度数组对应的一维数组
|
||
int *g = f[n - 1][k - 1];
|
||
int i = N - 1; // 最高位
|
||
while (!g[i]) i--; // 去除前导0
|
||
while (i >= 0) cout << g[i--]; // 倒序输出
|
||
return 0;
|
||
}
|
||
``` |