hdu 6333 Harvest of Apples 分块大法好,

There are n apples on a tree, numbered from 1 to n.
Count the number of ways to pick at most m apples.

Input
The first line of the input contains an integer T (1≤T≤105) denoting the number of test cases.
Each test case consists of one line with two integers n,m (1≤m≤n≤105).

Output
For each test case, print an integer representing the number of ways modulo 109+7.

Sample Input
2
5 2
1000 500

Sample Output
16
924129523

题意:计算c(n,0),c(n,1),,,c(n,m);
做法:分块,B = 333,预处理dp[i][j]代表c(i*B,j),用逆元需要n*B的复杂度。
然后对于c(n,m),可以把n分成两部分,一部分为p=n/B*B;一部分是q = n%B,
那么有c(n,m) = c(p,x)*c(q,y){x+y == m}成立,那么可以推出c(n,0)+c(n,1),,,+c(n,m) = c(p,x)*c(q,y){x+y <= m} 成立;因为q< B;
所以预处理c(p,x) 的前缀和,枚举y的值,就可以在n*B的复杂度下得到答案了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N= 1e5+100;
const int mod = 1e9+7;
int bp[335][335];
int dp[335][N];
ll f[N],invf[N],inv[N];

void add(int &x,int y){
    x += y;
    if(x >= mod) x -= mod;
}

void init(){
    dp[0][0] = 1;
    inv[1] =  1;
    f[0] =  invf[0] = 1;
    for(int i = 2;i < N;i ++){
        inv[i] = (mod-mod/i)*1LL*inv[mod%i]%mod;
    }
    for(int i =1;i < N;i ++){
        f[i] = f[i-1]*i%mod;
    }
    for(int i = 1;i < N;i ++){
        invf[i] = invf[i-1]*1LL*inv[i]%mod;
    }
    for(int i = 1;i < 100333/333;i ++){
        for(int j = 0;j <= 1e5;j ++){
            dp[i][j] = f[i*333]*invf[j]%mod*invf[i*333-j]%mod;
        }
    }
    for(int i = 1;i < 100333/333;i ++){
        for(int j = 1;j <= 1e5;j ++){
            add(dp[i][j],dp[i][j-1]);
        }
    }
    bp[0][0] = 1;
    for(int i = 1;i <= 333;i ++){
        for(int j= 0;j<= 333;j ++){
            bp[i][j] = bp[i-1][j];
            if(j) add(bp[i][j],bp[i-1][j-1]);
        }
    }
}


int main(){
    init();
    //cout << dp[1][2] << endl;
    int T;
    cin >>T;
    while(T--){
        int n,m;
        scanf("%d %d",&n,&m);
        int b = n/333;
        int res = n-b*333;
        int ans = 0;
        if(n <= 333) {
            for(int i = 0;i <= m;i ++){
                add(ans,bp[n][i]);
            }
        }
        else{
            int fir = 0;
            if(m <= b*333) fir = 0;
            else fir = m%333;
            for(int i = 0;i <= min(res,m);i ++){
                add(ans,1LL*bp[res][i]*dp[b][min(m-i,b*333)]%mod);
            }
        }

        printf("%d\n",ans);
    }
    return 0;
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值