HDU 6333 Problem B. Harvest of Apples(莫队算法+组合数)

本文介绍了一种使用莫队算法解决苹果收获计数问题的方法,该问题要求计算从一棵有编号的苹果树上最多摘取m个苹果的不同方式的数量,并通过预处理阶乘和逆元实现了O(Tsqrt(MAX))的时间复杂度。

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

Problem B. Harvest of Apples

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 2671    Accepted Submission(s): 1045


 

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

 

 

Recommend

chendu   |   We have carefully selected several similar problems for you:  6343 6342 6341 6340 6339 

 

因为要求的是组合数的和,我们定义S(n,m)为C(n,0)~C(n,m)的累加和,然后可以推导出以下变换方程:

S(n+1, m) = 2S(n, m) - C(n, m)   S(n-1, m) = (S(n, m) + C(n-1, m)) / 2

S(n, m+1) = S(n, m) + C(n, m+1)   S(n, m-1) = S(n, m) - C(n, m)

下面两个是很容易看出来的,上面的两个则利用我们熟知的组合数递推方程C(n,m)=C(n-1,m-1)+C(n-1,m)可以推出

也就是说,如果我们已知S(n,m)就可以在O(1)的时间之内得到S(n-1,m),S(n+1,m),S(n,m-1),S(n,m+1),这看上去就跟区间操作一样了。所以用莫队算法即可,因为要求的是组合数,所以要预处理一下阶乘和逆元。复杂度为O(Tsqrt(MAX))

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+5;
ll fac[maxn],inv[maxn],ans[maxn],bl[maxn];
ll res,l,r;
struct mo
{
    int l,r,id;
    bool operator<(const mo &a)const
    {
        if(bl[l]==bl[a.l]) return r<a.r;
        return l<a.l;
    }
}q[maxn];
void init()
{
    inv[0]=fac[0]=inv[1]=fac[1]=1;
    for(int i=1;i<maxn;i++)
    fac[i]=fac[i-1]*i%mod;
    for(int i=2;i<maxn;i++)
    inv[i]=(mod-(mod/i))*inv[mod%i]%mod;//lucas?????
    for(int i=1;i<maxn;i++)
    inv[i]=inv[i-1]*inv[i]%mod;
}

ll C(int n,int m)
{
    ll res=fac[n]*inv[m]%mod;
    res=res*inv[n-m]%mod;
    return res;
}

void update(int x,int y)
{
    if(x==1)
    {
        if(y==1)
            res=(2*res-C(l,r)+mod)%mod;
        else
            res=(res+C(l-1,r))*inv[2]%mod;

    }
    else
    {
        if(y==1)
            res=(res+C(l,r+1))%mod;
        else res=(res-C(l,r)+mod)%mod;
    }
}
int main()
{
    int t;
    init();
    scanf("%d",&t);
    int block=(int)sqrt(maxn);
    int up=0;
    for(int i=1;i<=t;i++)
    {
        scanf("%d %d",&q[i].l,&q[i].r);
        q[i].id=i;
        up=max(up,q[i].r);
    }
    for(int i=1;i<=up;i++)
    {
        bl[i]=(i-1)/block;
    }
    sort(q+1,q+t+1);
    res=1;
     l=0,r=0;
    for(int i=1;i<=t;i++)
    {
        while(l<q[i].l) update(1,1),l++;
        while(l>q[i].l) update(1,-1),l--;
        while(r<q[i].r) update(-1,1) ,r++;
        while(r>q[i].r) update(-1,-1) ,r--;
        ans[q[i].id]=res;
    }
    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、付费专栏及课程。

余额充值