Problem B. Harvest of Apples(莫队离线)
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 1792 Accepted Submission(s): 699
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≤10^5) denoting the number of test cases.
Each test case consists of one line with two integers n,m (1≤m≤n≤10^5).
Output
For each test case, print an integer representing the number of ways modulo 10^9+7.
Sample Input
2
5 2
1000 500
Sample Output
16
924129523
题意
求 C(n,i)。
解题思路
开始拿到这个题的时候还有些窃喜,诶,组合数求和,逆元搞一搞就行了,多捞哦!然后仔细一看数据范围,O(n^2)算法复杂度必超,尝试性的挑战了一下后台样例,果断TLE。然后我TM就找规律去了,woc杨辉三角,有点搞头,然后就没有然后了。。
以前没有学过莫队算法,这真的是一种神奇的暴力解题方式。因为这题没有涉及到数据更新,所以我们可以把查询离线,然后分块。又因为(我们这里令 C(n,i)=S(n,m))
S(n+1,m)=2*S(n,m)-C(n,m)
S(n-1,m)=S(n,m)+C(n-1,m)
S(n,m+1)=S(n,m)+C(n,m+1)
S(n,m-1)=S(n,m)-C(n,m)
PS:如果我们知道区间[L,R],就能在比较短的时间内求出[L−1,R],[L+1,R],[L,R−1],[L,R+1]的话,那就可以用莫队算法了。具体看代码吧。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+50;
const int mod = 1e9+7;
long long inv[maxn],fac[maxn];
long long quickpow(long long a, long long b)
{
long long ret = 1;
a %= mod;
while(b)
{
if (b & 1) ret = (ret * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return ret;
}
void init()
{
fac[0]=inv[0]=1;
for(int i=1; i<maxn; i++)
{
fac[i]=(fac[i-1]*i)%mod; //得到阶乘
inv[i]=quickpow(fac[i],mod-2); //得到阶乘的逆元
}
}
int pos[maxn];//块
struct node
{
int n,m,id;
} p[maxn];
int cmp(node a,node b)//让相邻两个操作的操作次数尽可能的小
{
if(pos[a.n]==pos[b.n]) return a.m<b.m;
return pos[a.n]<pos[b.n];
}
int n,m;
long long ans,res[maxn];
long long getnumber(int n,int m)
{
if(n<m) return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void addn(int n,int m) /*S(n+1,m)=2*S(n,m)-C(n,m)*/
{
ans=(2*ans%mod-getnumber(n,m)+mod)%mod; //不加mod取模会错
}
void addm(int n,int m) /*S(n-1,m)=S(n,m)+C(n-1,m)*/
{
ans=(ans+getnumber(n,m+1))%mod;
}
void subn(int n,int m) /*S(n,m+1)=S(n,m)+C(n,m+1)*/
{
ans=(ans+getnumber(n-1,m))*inv[2]%mod;
}
void subm(int n,int m) /*S(n,m-1)=S(n,m)-C(n,m)*/
{
ans=(ans-getnumber(n,m)+mod)%mod; //ts
}
int main()
{
#ifdef DEBUG
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif // DEBUG
init();
int t,block=(int)sqrt(1.0*maxn);
scanf("%d",&t);
for(int i=1; i<=t; i++) scanf("%d%d",&p[i].n,&p[i].m),p[i].id=i,pos[i]=i/block;
sort(p+1,p+1+t,cmp);
n=1,m=0,ans=1;
for(int i=1; i<=t; i++)
{
while(n<p[i].n) addn(n++,m);
while(n>p[i].n) subn(n--,m);
while(m<p[i].m) addm(n,m++);
while(m>p[i].m) subm(n,m--);
res[p[i].id]=ans;
}
for(int i=1; i<=t; i++) printf("%lld\n",res[i]);
return 0;
}