题意: 给你一个n,m 让你求C(n,0)+C(n,1)+...+C(n,m) 的值
一开始直接想到二项式方程 但发现2^n=C(n,0)+C(n,1)+...+C(n,n) 没什么用
后来发现队友开始吹 发现 用s(n,m)表示C(n,0)+C(n,1)+...+C(n,m) 那么
s(n,m)=2*s(n-1,m)-c(n-1,m);
s(n,m)=s(n,m-1)+c(n,m);
也就是说知道一个s(n,m)就能O(1)求出s(n+1,m),s(n-1,m),s(n,m+1),s(n,m-1)
队友一看就想到用莫队写 然而tle了 一直没做出来 看了题解才知道 我们每次用逆元时都 log(n)处理了 其实可以O(n)全部预处理出来。
接下来是官方代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200000;
const int MOD = 1000000007;
struct query
{
int n, k, i;
} Q[N];
int T;
vector <query> lst[N];
int cnt, mx, chunk;
int fac[N], inv[N], res[N], in_chunk[N];
int powi(int a, int b)
{
int c = 1;
for (; b; b >>= 1, a = 1ll * a * a % MOD)
if (b & 1) c = 1ll * c * a % MOD;
return c;
}
int C(int a, int b)
{
return 1ll * fac[a] * inv[b] % MOD * inv[a - b] % MOD;
}
int comp(query a, query b)
{
return a.n < b.n;
}
int main()
{
mx = 100000;
fac[0] = 1;
for (int i = 1; i <= mx; ++ i) //预处理100000内的阶乘
{
fac[i] = 1ll * fac[i - 1] * i % MOD;
}
inv[mx] = powi(fac[mx], MOD - 2);
for (int i = mx - 1; ~i; -- i) //预处理100000内的逆元 从最大的开始求
{
inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}
chunk = sqrt(mx); // 每块的大小
cnt = 1;
for (int i = 1; i <= mx; i += chunk, ++ cnt) // 开始分块 标记每个位置属于哪块
for (int j = i; j < i + chunk && j <= mx; ++ j)
in_chunk[j] = cnt;
cnt --;
scanf("%d", &T);
for (int i = 1; i <= T; ++ i)
{
scanf("%d%d", &Q[i].n, &Q[i].k), Q[i].i = i;
lst[in_chunk[Q[i].k]].push_back(Q[i]);
}
for (int i = 1; i <= cnt; ++ i) if (lst[i].size())
{
sort(lst[i].begin(), lst[i].end(), comp);
int val = 0, in = lst[i][0].n, ik = -1;
for (int j = 0; j < lst[i].size(); ++ j)
{
while (in < lst[i][j].n) val = (0ll + val + val + MOD - C(in ++, ik)) % MOD; //s(n,m)=2*s(n-1,m)-c(n-1,m);
while (ik < lst[i][j].k) val = (val + C(in, ++ ik)) % MOD; //s(n,m)=s(n,m-1)+c(n,m);
while (ik > lst[i][j].k) val = (val + MOD - C(in, ik --)) % MOD; //s(n,m-1)=s(n,m)-c(n,m);
res[lst[i][j].i] = val;
}
}
for (int i = 1; i <= T; ++ i) printf("%d\n", res[i]);
}