题意:
给你n,m,让你求出从0到m组合数C(n,m)的和。
题解:
比赛的时候用了好多求组合数的模板,优化了好久,tle到自闭。。。
首先我们要利用预处理阶乘逆元:
ll getpow(ll a, int b){
ll sum = 1;
while(b){
if(b & 1){
sum = (sum * a) % mod;
}
a = (a*a) % mod;
b >>= 1;
}
return sum;
}
ll C(int n, int m){
return fac[n]*inv[m]%mod * inv[n-m]%mod;
}
void init(){
temp = getpow(2, mod-2);
fac[0] = inv[0] = 1;
for(int i = 1; i < maxn; i++){
fac[i] = (i*fac[i-1]) % mod;
inv[i] = getpow(fac[i], mod-2);
}
}
这样我们就可以快速获得组合数C(n, m)的值。
画一个杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
这样我们可以得到:
s(5,2) = 16, s(5,1) = 10, s(4,2) = 11, c(4,2) = 6, c(5,2) = 10;
s(5,2) = s(4,2)*2-c(4,2), s(5,2) = s(5,1)+c(5,2);
所以s(n,m) = s(n-1, m)*2-c(n-1, m), s(n, m) = s(n, m-1) + c(n, m)。
这样我们就可以用莫队来离线搜索答案区间。
代码如下:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5+10;
const int mod = 1e9+7;
ll fac[maxn], inv[maxn], ans[maxn], pos[maxn];
ll temp, unit, n;
int t;
struct Query{
int l, r, id;
bool operator <(const Query p){
if(pos[l] == pos[p.l]){
return r < p.r;
}
return l < p.l;
}
}node[maxn];
ll getpow(ll a, int b){
ll sum = 1;
while(b){
if(b & 1){
sum = (sum * a) % mod;
}
a = (a*a) % mod;
b >>= 1;
}
return sum;
}
ll C(int n, int m){
return fac[n]*inv[m]%mod * inv[n-m]%mod;
}
void init(){
temp = getpow(2, mod-2);
fac[0] = inv[0] = 1;
for(int i = 1; i < maxn; i++){
fac[i] = (i*fac[i-1]) % mod;
inv[i] = getpow(fac[i], mod-2);
}
}
void work(){
ll sum = 2;
int L = 1, R = 1;
for(int i = 1; i <= t; i++){
while(L < node[i].l){
L++;
sum = (2*sum%mod - C(L-1, R) + mod) % mod;
}
while(L > node[i].l){
sum = (sum + C(L-1, R)) % mod * temp % mod;
L--;
}
while(R < node[i].r){
R++;
sum = (sum + C(L, R)) % mod;
}
while(R > node[i].r){
sum = (sum - C(L, R) + mod) % mod;
R--;
}
ans[node[i].id] = sum;
}
}
int main(){
scanf("%d", &t);
int n, m;
init();
unit = (int)sqrt(maxn*1.0);
for(int i = 1; i <= t; i++){
scanf("%d %d", &node[i].l, &node[i].r);
node[i].id = i;
pos[i] = i/unit;
}
sort(node+1, node+1+t);
work();
for(int i = 1; i <= t; i++){
printf("%lld\n", ans[i]);
}
}