题意是给你T个n,m求C(n,m)的前缀和。
定义S(n,m)为前缀和,则考虑已知S(n,m)的情况下能得到什么式子。经过一番简单推导可得:S(n,m)=S(n,m-1)+C(n,m)以及S(n,m)=2*S(n-1,m)-C(n-1,m)。因此我们可以用O(1)的时间转移。因此考虑莫队算法。
以前对莫队算法的理解局限在了区间询问,这题开了眼界了233
#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<cstdio>
#include<stdio.h>
#include<set>
#include<map>
#include<deque>
#include<stack>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<list>
#include<bitset>
#include<sstream>
using namespace std;
#define lc n << 1
#define rc n << 1 | 1
#define pb push_back
#define mp make_pair
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int maxn = 1000010;
const int mod = 1e9 + 7;
const int INF = 0X3f3f3f3f;
int sz;
struct qry {
int n, m;
int id;
bool operator <(const qry& y)
{
if (n / sz == y.n / sz)return m < y.m;
return n / sz < y.n / sz;
}
}q[maxn];
ll inv[maxn], jc[maxn], ans, aa[maxn];
int main()
{
jc[0] = jc[1] = 1;
inv[1] = 1;
for (int i = 2;i < 100001;i++)
{
inv[i] = inv[mod%i] * (mod - mod / i) % mod;//逆元打表
jc[i] = jc[i - 1] * i % mod;
}
for (int i = 2;i < 1e5 + 1;i++)
inv[i] = inv[i - 1] * inv[i] % mod;//阶乘逆元
inv[0] = 1;
int T, n, m;
scanf("%d", &T);
sz = 0;
for (int i = 0;i < T;i++)
{
scanf("%d%d", &q[i].n, &q[i].m);
q[i].id = i;
sz = max(sz, q[i].n);
}
sz = sqrt(sz);
sort(q, q + T);
n = 1, m = 0, ans = 1;//ans = S(n,m) = sigma_{1<=i<=n}C(n,i)
auto C = [](int n, int m) {return jc[n] * inv[m] % mod * inv[n - m] % mod;};
for (int i = 0;i < T;i++)
{
while (n < q[i].n) { n++; ans = ans * 2 - C(n - 1, m) + mod;ans %= mod; }
while (m < q[i].m) { m++; ans = ans + C(n, m);ans %= mod; }
while (m > q[i].m) { ans = ans - C(n, m) + mod;ans %= mod;m--; }
while (n > q[i].n) { ans = ans + C(n - 1, m);ans = ans * inv[2] % mod;n--; }
aa[q[i].id] = ans;
}
for (int i = 0;i < T;i++)
printf("%lld\n", aa[i]);
return 0;
}