求不超过long long范围的所有组合数(分类讨论+杨辉三角打表)

题目大意

给定 q ( 1 ≤ q ≤ 1 0 6 ) q(1 \leq q \leq 10^6) q(1q106),每次输入两个数 n , m ( 1 ≤ n ≤ 2 63 − 1 , 0 ≤ m ≤ m ) n,m(1 \leq n \leq 2^{63} - 1, 0 \leq m \leq m) n,m(1n2631,0mm),若 C n m ≤ 2 63 − 1 C_n^m \leq 2^{63} - 1 Cnm2631 输出 C n m C_n^m Cnm,否则输出0。

解题思路

在杨辉三角预处理组合数时,到第70项以后开始就出现了大于 2 63 − 1 2^{63}-1 2631 的数,第 1000 项的第 9 个组合数就已经超过范围了。而根据杨辉三角的递推式 C n m = C n − 1 m − 1 + C n − 1 m C_n^m = C_{n - 1}^{m - 1} + C_{n - 1}^{m} Cnm=Cn1m1+Cn1m,若上面两项的其中一项已经溢出,那么下面的项后面就不需要再递推了。

但是即使这样数组也不能开太大,于是想了一个思路,就是先找到 C n 4 C_n^4 Cn4 不溢出的分解数,即145056,然后再找到 C n 3 C_n^3 Cn3 不溢出的分界数 4801280,大于4801280的数实际上只可能有三种方式,特判即可。小于145056的直接打表,但不是全部的表,1000项之后的表只需要开前十项的空间。

记得开 _ _ i n t 128 \_\_int128 __int128

#include <bits/stdc++.h>

using namespace std;
#define ENDL "\n"
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double eps = 1e-4;
const int Mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int maxn = 150000;
const ull INF = ~0ULL;

__int128_t C[1005][1005];
__int128_t C2[maxn][11];
int len[maxn];

void write(__int128_t x) {
    if (x < 0) cout << "-", x = -x;
    if (x < 10) {
        cout << (char)('0' + x);
        return;
    }
    write(x / 10);
    cout << (char)('0' + x % 10);
}

void init() {
    C[0][0] = 1;
    len[0] = 0;
    for (int i = 1; i <= 145056; i++) {
        if (i < 1000) {
            C[i][0] = 1;
            for (int j = 1; j <= i; j++) {
                C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
                if (C[i][j] > INF) {
                    len[i] = j - 1;
                    break;
                }
            }
        } else if (i == 1000) {
            C2[i][0] = 1;
            for (int j = 1; j <= i; j++) {
                C2[i][j] = C[i - 1][j] + C[i - 1][j - 1];
                if (C2[i][j] > INF) {
                    len[i] = j - 1;
                    break;
                }
            }
        } else {
            C2[i][0] = 1;
            for (int j = 1; j <= i; j++) {
                C2[i][j] = C2[i - 1][j] + C2[i - 1][j - 1];
                if (C2[i][j] > INF) {
                    len[i] = j - 1;
                    break;
                }
            }
        }
        if (!len[i]) len[i] = i;
    }
    // for (int i = 1; i <= 1000; i++) cout << len[i] << endl;
}

__int128_t CN2(__int128_t x) { return x * (x - 1) / 2; }

__int128_t CN3(__int128_t x) { return x * (x - 1) * (x - 2) / 6; }

__int128_t CN4(__int128_t x) { return x * (x - 1) * (x - 2) * (x - 3) / 24; }

// 0 - 145056

// 145057 - 4801280  最大C_i^3

// 4801281 检测C_i^2是否越界

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    ll n, m;
    ll s = 1e10;
    init();
    int up = 1e6;
    for (int i = 1; i <= up; i++) {
        cin >> n >> m;
        if (m > n / 2) m = n - m;
        if (n <= 145056) {
            if (m <= len[n]) {
                if (n < 1000)
                    write(C[n][m]), cout << ENDL;
                else
                    write(C2[n][m]), cout << ENDL;
            } else
                cout << 0 << ENDL;
        } else if (n > 145056 && n <= 4801280) {
            if (m == 3) {
                if (CN3(n) <= INF)
                    write(CN3(n)), cout << ENDL;
                else
                    cout << 0 << ENDL;
            } else if (m == 2)
                write(CN2(n)), cout << ENDL;
            else if (m == 1)
                cout << n << ENDL;
            else if (m == 0)
                cout << 1 << ENDL;
            else
                cout << 0 << ENDL;
        } else if (n >= 4801281 && n < s) {
            if (m == 2) {
                if (CN2(n) <= INF)
                    write(CN2(n)), cout << ENDL;
                else
                    cout << 0 << ENDL;
            } else if (m == 1)
                cout << n << ENDL;
            else if (m == 0)
                cout << 1 << ENDL;
            else
                cout << 0 << ENDL;
        } else {  //只有CN1和CN0
            if (m == 1)
                cout << n << ENDL;
            else if (m == 0)
                cout << 1 << ENDL;
            else
                cout << 0 << ENDL;
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值