HDU 6333 Problem B. Harvest of Apples(莫队+线性求组合数取模(阶乘逆元))

Problem B. Harvest of Apples

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

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

扫描二维码关注公众号,回复: 2704411 查看本文章

1000 500

Sample Output

 

16

924129523

 

题意:

让你求C(n,0)+C(n,1)+....+C(n,m)

然后结果模上1e9+7

解析:

定义 S(n, m) = \sum_{i = 0} ^ {m} {n \choose i},不难发现  S(n, m) = 2S(n - 1, m) - {n - 1 \choose m}。也就是说,如果我们知道 S(n, m),就能以 O(1) 的代价计算出 S(n−1,m),S(n+1,m),S(n,m+1)S(n, m-1), ,可以采用莫队算法。

时间复杂度 O(T \sqrt{MAX})

O(1)时间求组合数的办法就是阶乘逆元打表,a^{-1} * b^{-1} = (a*b)^{-1},所以先预处理出1-1e5的逆元,然后在求乘起来就好了。

这里还有就不得不说一说杭电C++的编译器了...一样的代码用c++就T,2s多,用G++就A了,只要0.3s....真的无话可说....

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include <vector>
#include<algorithm>
using namespace std;

typedef long long int ll;

const int MAXN = 1e5+10;
const ll MOD = 1e9+7;

int n,m;
ll ans;
ll two;
ll jc[MAXN];
ll inv[MAXN];
int pos[MAXN];  //i位置是属于第pos[i]块的,用于对询问进行排序
int res[MAXN];

struct node{
    int l;
    int r;
    int id;
    ll ans;

}q[MAXN];



bool cmp(node a,node b)
{
    if(pos[a.l]==pos[b.l])    //比较与下面的代码,快了0.2s左右,也可能是数据的原因
	{                        //就是相邻块之间不用重新跳到开头,例如(4,8)和(10,1),(10,8),这样就可以算完(4,8)直接算(10,8)不用从头开始了,即不用把r=8变成r=1
		return pos[a.l]&1?(a.r<b.r):(a.r>b.r);     //
	}
	/*if(pos[a.l]==pos[b.l])
	{
		return a.r<b.r;
	}*/
	return a.l<b.l;
}

bool cmp1(node a,node b)
{
    return a.id<b.id;
}

inline ll C(int a, int b)    //计算C(a, b),a下,b上
{
	if(b>a) return 0;
    return jc[a] * inv[b] % MOD
        * inv[a-b]%MOD;
}

inline void update1(int l,int r)
{
    ans=(2*ans-C(l,r)+MOD)%MOD;
}

inline void update2(int l,int r)
{
	ans=((ans+C(l,r))%MOD)*two%MOD;
}

inline void update3(int l,int r)
{
	ans=(ans-C(l,r)+MOD)%MOD;
}

inline void update4(int l,int r)
{
	ans=(ans+C(l,r))%MOD;
}

inline void solve()
{
    int i,l,r;
    l=1;r=0;
    ans=1;
    for(i=1;i<=m;i++)
    {
        while(l<q[i].l) update1(l,r),l++;  //将l向右移至q[i].l
        while(l>q[i].l) update2(l-1,r),l--;   //将l向左移至q[i].l
        while(r>q[i].r) update3(l,r),r--;     //将r左移至q[i].r
        while(r<q[i].r) update4(l,r+1),r++;    //将r右移至q[i].r
		res[q[i].id]=ans;
    }

}

void init()
{
	ll p=1;
	jc[0]=1;
	jc[1]=1;
	inv[0]=1;
	inv[1]=1;
	for(int i=2;i<MAXN;i++)
	{
		p=(p*i)%MOD;
		jc[i]=p;
		//inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	}
	
	for(int i=2;i<MAXN;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;   //O(n)求逆元
	for(int i=2;i<MAXN;i++) inv[i]=inv[i-1]*inv[i]%MOD;   //扩展到i!的逆元
}

int main()
{
		int k;
		
		init();
		two=inv[2];
		scanf("%d",&m);
		n=100000;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&q[i].l,&q[i].r);
            //q[i].ans=-1;
            q[i].id=i;
			//pos[q[i].l]=q[i].l/block;
        }
		 int block=sqrt(n);
		 for(int i=1;i<=m;i++)
			 if(pos[q[i].l]==0)
			 pos[q[i].l]=(q[i].l/block)+1;

        sort(q+1,q+m+1,cmp);  //按照块来排序
        solve();
        //sort(q+1,q+m+1,cmp1);
        for(int i=1;i<=m;i++)
        {
            printf("%d\n",res[i]);
        }
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/81383974