Harvest of Apples

本文介绍了一种基于组合数学原理的算法,用于计算从编号1到n的苹果树上最多摘取m个苹果的不同方式的数量,并通过使用快速幂、逆元等技术优化计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题解:题目要求累加组合C(n,m)m的范围(0,m);所以如果用莫队最重要的是 弄清相邻的总和之间的关系

         并且要明白组合是如何在编程中实现的,例如C(n,m)为1到n的累乘,乘以n-m的逆元进行mod运算,乘以m的逆元再进行mod运算。

         逆元则是当前数的mod-2次方,最好在用之前就把累乘表和逆元表打好;

#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define maxn 100005
#define ll long long
ll inv[maxn],fas[maxn],ans[maxn];
int pos[maxn];//记录数据所属的块
struct node
{
    int n,m,id;
}a[maxn];
bool cmp(node o,node p)
{
    return pos[o.n]==pos[p.n]?o.m<p.m:o.n<p.n;
}
ll qpow(ll a,ll b)//快速幂
{
    ll ans=1;
    while(b)
    {
        if(b&1)
        {
           ans=ans*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
void init()
{
    fas[1]=1;
    for(int i=2;i<maxn;i++)//累乘表
    {
        fas[i]=i*fas[i-1]%mod;
    }
    for(int i=1;i<maxn;i++)//逆元表
    {
        inv[i]=qpow(fas[i],mod-2);
    }
}
ll C(int n,int m)//组合运算
{
    if(n<=0||n<m||m<0)
    {
        return 0;
    }
    if(m==0||n==m)
    {
        return 1;
    }
    return fas[n]*inv[n-m]%mod*inv[m]%mod;
}
int main()
{
    init();
    int block=sqrt(maxn),t;
    cin>>t;
    for(int i=1;i<maxn;i++)//进行莫队的划块
    {
        pos[i]=(i-1)/block;
    }
    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,cmp);//莫队排序
    int st=1,en=0;
    ll dns=1;
    for(int i=1;i<=t;i++)
    {
        while(st<a[i].n)
        {
            dns=(2*dns-C(st++,en)+mod)%mod;
        }
        while(st>a[i].n)
        {
            dns=(dns+C(--st,en))*inv[2]%mod;
        }
        while(en<a[i].m)
        {
            dns=(dns+C(st,++en))%mod;
        }
        while(en>a[i].m)
        {
            dns=(dns-C(st,en--)+mod)%mod;
        }
        ans[a[i].id]=dns;
    }
    for(int i=1;i<=t;i++)//输出结果
    {
        printf("%lld\n",ans[i]);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值