HDU 6333 Problem B. Harvest of Apples

Problem Description

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

 

 

Source

2018 Multi-University Training Contest 4

 

 

这个题的题意很简单,就是给你n个苹果,让你取最多m个,那么就是\sum_{i=0}^{m}(C(n,i)),如果直接求的话,肯定会超时,所以为了节省时间,要用其他方法求组合数,首先把1到n的阶乘取模存到一个数组 Jc[] 中,然后用拓展欧几里得定理或者费马小定理+快速幂求Jc[i]的逆元x1;然后Jc[i]*x1%mod记为x2,求jc[n-i]的逆元x3,然后C(n,i)=x2*x3%mod,时间复杂度能降低一点,大概是O(nlogn),这些都是辅助作用(-_-||其实也很有用),接下来就是重点,莫队算法,莫队算法主要是用来解决一些离线无修改的区间查询问题,实现起来相比比较的简单。而莫队算法的主要用在线段树等数据结构无法在很短时间内实现区间信息合并的情况。具体的可以百度,这里就不讲了,还有就是求组合数和的公式S(n,m)=2*S(n-1,m)-C(n-1,m),这个由杨辉三角就能看出来

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const ll N = 1e5 + 7;
ll fac[N], pr[N], inv[N], unit, ans, m, rev2;
ll qpow(ll a, ll b)//快速幂
{
	ll ans = 1; a %= mod;
	for (ll i = b; i; i >>= 1, a = a * a%mod)
		if (i & 1)ans = ans * a%mod;
	return ans;
}
ll C(ll n, ll m) //求C(n,m)
{
	return fac[n] * inv[n - m] % mod*inv[m] % mod;
}
struct Node //因为莫队是离线的,所以用结构体来存每一次输入要查询的区间,将查询结果储存,统一输出
{
	ll n, m, id;//id表示这次询问是第几次输入的
	bool operator<(const Node a)const 
	{
		return n / unit == a.n / unit ? m < a.m : n < a.n;
	}
}q[N];
void init() //求逆元
{
	rev2 = qpow(2, mod - 2);
	fac[0] = 1;
	for (ll i = 1; i < N; i++)
		fac[i] = fac[i - 1] * i%mod;
	inv[N - 1] = qpow(fac[N - 1], mod - 2);
	for (ll i = N - 2; i >= 0; i--)
		inv[i] = inv[i + 1] * (i + 1) % mod;
}
int main()
{
	init();
	scanf("%lld", &m);
	for (ll i = 1; i <= m; i++)
	{
		scanf("%lld%lld", &q[i].n, &q[i].m);
		q[i].id = i;
	}
	unit = (ll)sqrt(1.0*N);
	sort(q + 1, q + 1 + m);
	ans = 2;
	ll nn = 1, mm = 1;
	for (ll i = 1; i <= m; i++)
	{
		while (nn < q[i].n)
		{
			nn++;//如果nn小于q[i].n,说明nn不在q[i].n所在的块内
			ans = (2 * ans%mod - C(nn - 1, mm) + mod) % mod;
		}
		while (mm < q[i].m) 
		{
			mm++;
			ans = (ans + C(nn, mm)) % mod;
		}
		while (nn > q[i].n)
		{
			ans = (ans + C(nn - 1, mm)) % mod*rev2%mod;
			nn--;
		}
		while (mm > q[i].m)
		{
			ans = (ans - C(nn, mm) + mod) % mod;
			mm--;
		}
		pr[q[i].id] = ans;
	}
	for (ll i = 1; i <= m; i++)
		printf("%lld\n", pr[i]);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值