HDU - 6333 - Problem B. Harvest of Apples (排列组合+莫队)

HDU - 6333 - Problem B. Harvest of Apples

题意:

一颗树上有n颗苹果,求取不多于m个苹果有多少种拿法

 

F(n,m) = \sum_{k=0}^{k=m}C_{n}^{k},F(n, m)即为n颗苹果拿不多于m个的方法的种数且F(n,m) = F(n,m-1)+C_n^m

可以打个表或者手算出前面几行,就可以发现 F(n, m)=F(n-1,m)+F(n-1,m-1)

已知F(n,m)就可以推出 F(n-1, m),F(n+1, m),F(n, m-1),F(n, m+1)

F(n, m)=F(n-1,m)+F(n-1,m-1)=2F(n-1,m)-C_n^m

 

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <algorithm>

using namespace std;
typedef long long int LL;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
const int block = sqrt(N);
int T;
LL fact[N], inv[N], tot, n, m;
struct node{
    int n, m, id;
    LL ans;
}a[N];
bool cmp1(node a,node b){ // 按块排序
    if(a.n/block != b.n/block) return a.n/block < b.n/block;
    return a.m/block < b.m/block;
}
bool cmp2(node a,node b){
    return a.id < b.id;
}
LL qkm(LL base, LL mi, LL mod){
    LL ans = 1;
    while(mi){
        if(mi&1) ans = ans * base % mod;
        base = base * base % mod;
        mi >>= 1;
    }
    return ans;
}
void init() {
    n = 1; m = 0; tot = 1; // 刚开始为f(1,0)
    // 阶乘逆元打表
    int MAXN = N - 1;
    fact[0] = 1;
    for(int i=1;i<=MAXN;i++) fact[i] = fact[i-1] * i % mod;
    inv[N-1] = qkm(fact[MAXN], mod - 2, mod);
    for(int i = MAXN - 1; i >= 0; i--)
        inv[i] = (inv[i + 1] * (i + 1)) % mod;
}
LL C(LL n, LL m){ // 求阶乘
    return fact[n] * inv[m] % mod * inv[n-m] % mod;
}
void slove(){
    // tot = f(n, m)
    // f(n, m) = f(n, m-1) + C(n, m)
    // f(n+1, m) = f(n, m) * 2 - C(n, m)
    for(int i=1;i<=T;i++){
        // m <= n
        // f(n, m) -> f(n, m-1)
        while(m > a[i].m) { // 先降m 再降n
            tot = (tot - C(n, m) + mod) % mod;
            m --;
        }
        // f(n, m) -> f(n-1, m)
        while(n > a[i].n) {
            tot = (tot + C(n-1, m)) * inv[2] % mod;
            n --;
        }
        // f(n, m) -> f(n+1, m)
        while(n < a[i].n){ // 先升n 再降m
            tot = (tot * 2 - C(n, m) + mod) % mod;
            n ++;
        }
        // f(n, m) -> f(n, m+1)
        while(m < a[i].m){
            tot = (tot + C(n, m+1)) % mod;
            m ++;
        }
        a[i].ans = tot;
    }
}
int main()
{
    init();
    scanf("%d", &T);
    for(int i=1;i<=T;i++) {
        scanf("%d%d",&a[i].n, &a[i].m);
        a[i].id = i;
    }
    sort(a+1, a+1+T, cmp1);
    slove();
    sort(a+1, a+1+T, cmp2);
    for(int i=1;i<=T;i++) printf("%lld\n", a[i].ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值