You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 1308. 方程的解

一、题目描述

佳佳碰到了一个难题,请你来帮忙解决。

对于不定方程 a_1+a_2+⋯+a_{k1}+a_k=g(x),其中 k≥1k∈Nx 是正整数,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)

输入样例

3 2

输出样例

3

二、隔板法求组合数问题

考虑问题:a_1+a_2+…+a_k=n,要求a_i(i\in [1,k]) 都是正整数 , 共有多少种\{a_1,a_2,…,a_k\} 的组合能够满足上述等式。

隔板法

  • 构造n个小球* * * * * * *,则n个小球共有n1个空隙

  • 每插入一个空隙,从该空隙向左直到遇到第一个板子或左边界为止的这个区间的和,记为该数a_i的值

  • 为实现把原区间分成 k 个子区间目标,共需插入k1个板子

举栗子 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_{n1}^{k1}

本题答案没有要求取模,答案上界的极限是C_{1000-1}^{100-1} 根据组合数学公式 $\displaystyle C_n^m=\frac{n!}{m! \cdot (n-m)!} =\frac{1000!}{100!\cdot 900!} \approx 10^{139} $

上面这个精度的估算,可以使用电脑自带的计算器,采用科学型就能计算阶乘了,记得算完第一个除法后按一下确定,yxc犯了这个错误~ 必爆intlong long,\_\_int128!

需要 高精度+递推,可以视为一个 模板

三、实现代码

#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最大值是1000k最大值是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;
}