HDU - 6333 - Problem B. Harvest of Apples
题意:
一颗树上有n颗苹果,求取不多于m个苹果有多少种拿法
令,F(n, m)即为n颗苹果拿不多于m个的方法的种数且
可以打个表或者手算出前面几行,就可以发现
已知F(n,m)就可以推出 F(n-1, m),F(n+1, m),F(n, m-1),F(n, m+1)
#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;
}