HDU 6333&&18多校4B Harvest of Apples 【分块+数学】

 

HDU6333   
 

Problem B. Harvest of Apples

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


 

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≤mn≤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

 


 
 

 

分析:开始想啦很久,没思路。然后队友突然说可以用离线,突然想到,然后推啦一下\sum_ {i=1}^{m}C(n,i)\sum _{i=1}^{m}C(n+1,m)的O(1)递推关系。

\sum_{i=1}^mC(n+1,m)=(2*\sum_{i=1}^mC(n,m))-C(n,m)

离线排个序看能不能快速求出,想到离散化m,感觉时间复杂度太大。然后突然想到可以分块,分200块,复杂度递推100*n.计算排序好的每个值,由于每个块大小是1000,可以通过O(500)的时间求出答案。时间复杂度O(500T+500n)。

昨天感觉能过就写啦,没多想,还有分块也不是我负责的方面,其实分\sqrt(N/2)块能达到最有效果。时间复杂度理论上能降低一半。

代码


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5;
const int ND=500;
const LL MOD=1e9+7;
const LL ny=500000004;
LL fac[N+10],inv[N+10],ans[N+10];
struct node
{
    int x,y,ind;
    bool operator<(const node& c)const
    {
        if(x==c.x)
            return y<c.y;
        return x<c.x;
    }
}a[N+10];
struct nd
{
    LL val,in;
}v[510];
LL pod(LL x,LL n,LL MOD)
{
    LL ret=1;
    while(n)
    {
        if(n&1)ret=ret*x%MOD;
        x=x*x%MOD;
        n>>=1;
    }
    return ret;
}
void init(int n)
{
    fac[0]=fac[1]=1;
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)
        fac[i]=fac[i-1]*i%MOD;
    inv[n]=pod(fac[n],MOD-2,MOD);
    for(int i=n;i>1;i--)
        inv[i-1]=inv[i]*i%MOD;
}
LL c(int n,int m)
{
    return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int main()
{

    init(N);
    int T;
    scanf("%d",&T);
    for(int i=1;i<=T;i++)scanf("%d%d",&a[i].x,&a[i].y),a[i].ind=i;
    sort(a+1,a+T+1);
    int x,y,ii,m=1,k;
    for(int i=1;i<=N;i++)
    {
        k=0;
        for(int j=0;j<i;j+=ND,k++)
        {
            if(j==0)
               v[k].val=1;
            else
            {
                v[k].val=(v[k].val*2+MOD-c(i-1,j))%MOD;
            }
            v[k].in=j;
        }

        v[k].val=pod(2ll,(LL)i,MOD);
        v[k].in=i;
        while(m<=T&&a[m].x==i)
        {
            ii=a[m].ind;
            x=a[m].y/ND;
            y=a[m].y%ND;

            if(y<=ND/2)
            {
                ans[ii]=v[x].val;
                for(int j=v[x].in+1;j<=a[m].y;j++)
                    ans[ii]=(ans[ii]+c(i,j))%MOD;
            }
            else
            {
                ans[ii]=v[x+1].val;
                for(int j=a[m].y+1;j<=v[x+1].in;j++)
                    ans[ii]=(ans[ii]+MOD-c(i,j))%MOD;
            }
            m++;
        }
    }
    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、付费专栏及课程。

余额充值