题目链接:https://vjudge.net/problem/HDU-6333
转自:https://blog.csdn.net/codeswarrior/article/details/81359075
题意:求组合数前缀和
思路:设n k 为下标和上标,那么可以想象成区间移动。(n-1,k) (n+1,k) (n,k-1)(n,k+1)都可以由(n,k)推导得来。
第三个公式的推导:
用莫队推就好了。
还要注意/2的时候要用逆元 可能是出现小数导致精度问题
c++tle g++可以过
虽然没被卡但是要注意上下标增减顺序 不能出现下标<上标的情况
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
long long fac[maxn],inv[maxn];
struct query
{
long long l,r,id,block;
bool operator < ( query p)const
{
if(block == p.block)
return r < p.r;
return block < p.block;
}
} q[maxn];
long long res;
long long ans[maxn];
long long qsm(long long a,long long b)
{
long long t=1;
while(b)
{
if(b&1)
t=t*a%mod;
b>>=1;
a=a*a%mod;
}
return t;
}
long long calc(int n,int k)//组合数计算
{
return fac[n]*inv[k]%mod*inv[n-k]%mod;
}
void init()//递推逆元和阶乘
{
fac[0]=fac[1]=1;
for(int i=2; i<maxn; i++)
fac[i]=(i*fac[i-1])%mod;
inv[maxn-1]=qsm(fac[maxn-1],mod-2);
for(int i=maxn-2; i>=0; i--)
inv[i]=(inv[i+1]*(i+1))%mod;
}
int main()
{
int T;
init();
scanf("%d",&T);
int len=sqrt(100000*1.0);//莫队块的大小
for(int i=1; i<=T; i++)
{
scanf("%lld%lld",&q[i].l,&q[i].r);
q[i].id=i;
q[i].block=q[i].l/len;
}
sort(q+1,q+1+T);
res=2;//一开始l=1,r=1,所以答案是2
int l=1,r=1;
long long inv2=qsm(2,mod-2);
for(int i=1; i<=T; i++)
{
while(l<q[i].l)
{
res=(2*res)%mod-calc(l++,r);
res+=mod;
res%=mod;
}
while(l>q[i].l)
{
res=(res+calc(--l,r))%mod;
res*=inv2;
res%=mod;
}
while(r<q[i].r)
{
res=res+calc(l,++r);
res%=mod;
}
while(r>q[i].r)
{
res=res-calc(l,r--)+mod;
res%=mod;
}
ans[q[i].id]=res;
}
for(int i=1; i<=T; i++)
{
printf("%lld\n",(ans[i]+mod)%mod);
}
return 0;
}