hdu6333 给出n和m,求C(n,0)+C(n,1)+...+C(n,m)的值。
把结果定义为S(n,m),可以得到S(n,m) = S(n-1,m) + S(n-1,m-1),由此就有S(n,m) = S(n,m-1) + C(n,m) = 2*S(n-1,m) - C(n,m)。
也就是说根据S(n,m)我们可以在O(1)的时间里求出S(n-1,m),S(n,m-1),S(n+1,m),S(n,m+1),于是就可以用莫队算法来做了。
莫队算法解决的问题就是对于n个数的数列,有若干(l,r)的询问,查询区间的某些性质。如果对于(l,r),可以O(1)内转移到(l,r-1),(l,r+1),(l+1,r),(l-1,r),就可以离线地处理这些询问,通过适当地改变运算顺序,在更优的时间复杂度内完成所有查询。
莫队算法首先将序列分成sqrt(n)个块,对查询的区间排序的时候,如果左端点在同一块里面就按右端点升序排序,否则就按左端点排序。这样按顺序处理的区间的时候可以使得转换的次数相对较小。以及此题里转移并不是简单的加减,需要使用上面的两个式子。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
const int maxn = 100007;
const ll mod = 1000000007;
int t, pos[maxn];
ll ans[maxn], inv[maxn], fac[maxn];
struct node
{
int n, m;
int id;
}e[maxn];
bool cmp(node a, node b)
{
if(pos[a.n] != pos[b.n]) return a.n < b.n;
return a.m < b.m;
}
ll qpow(ll a, ll x)
{
ll ans = 1;
while(x)
{
if(x & 1)
ans = (ans*a) % mod;
a = a*a % mod;
x >>= 1;
}
return ans;
}
void init()
{
int tmp = sqrt(maxn);
for(int i = 1;i < maxn;i++)
pos[i] = (i - 1)/tmp;
fac[0] = inv[0] = 1;
fac[1] = inv[1] = 1;
for(int i = 2;i < maxn;i++)
fac[i] = (fac[i - 1]*i) % mod;
for(int i = 2;i < maxn;i++)
inv[i] = qpow(fac[i], mod - 2);
//inv[i] = (inv[i + 1]*(i + 1)) % mod;
}
ll C(int n, int m)
{
if(n <= 0 || m < 0 || n < m)
return 0;
if(m == 0 || n == m)
return 1;
return fac[n]*inv[m] % mod*inv[n - m] % mod;
}
int main()
{
scanf("%d", &t);
init();
for(int i = 1;i <= t;i++)
{
scanf("%d%d", &e[i].n, &e[i].m);
e[i].id = i;
}
sort(e + 1, e + t + 1, cmp);
ll L = 1, R = 0;
ll res = 1;
for(int i = 1;i <= t;i++)
{
while(L < e[i].n) res = (res*2 - C(L++, R) + mod) % mod;
while(L > e[i].n) res = ((res + C(--L, R))*inv[2]) % mod;
while(R < e[i].m) res = (res + C(L, ++R)) % mod;
while(R > e[i].m) res = (res - C(L, R--) + mod) % mod;
ans[e[i].id] = res;
}
for(int i = 1;i <= t;i++)
printf("%lld\n", ans[i]);
return 0;
}