链接:https://ac.nowcoder.com/acm/contest/925/I
来源:牛客网
题目描述
n个不同的滑稽果中,每个滑稽果可取可不取,从所有方案数中选取一种,求选取的方案中滑稽果个数不超过m的概率。(对10+7取模)
输入描述:
第一行一个正整数T( T <= 10^5 ) 随后T行每行两个整数n,m ( 0 < m <= n <= 10^5 )
输出描述:
T行,每行一个整数表示答案。
示例1
输入
2
5 2
5 1
输出
500000004
687500005 题意:要你求取的数量不超过m的数量的概率
思路:首先因为组合数太大,我们就只能用Lucas 来求
其次这个数据范围太大,我们肯定不能直接暴力求出组合数
这里我们把 s(m,n) 设为 从n个中选取至多m个物品的方案数,我们可以得出 s(m,n) = s(m-1,n) + c(m,n)
我们还可以用杨辉三角得出 s(m,n)= 2 * s(m,n-1)*2-c(m,n-1)
s(4,8) = c(0,8) + c(1,8) + c(2,8) + c(3,8) + c(4,8)
s(4,7) = c(0,7) + c(1,7) + c(2,7) + c(3,7) + c(4,7)
我们可以借由定理:c(m,n)+c(m+1,n) = c(m+1,n+1)
c(0,8)=c(0,7)
c(1,8)=c(0,7)+c(1,7)
c(2,8)=c(1,7)+c(2,7)
c(3,8)=c(2,7)+c(3,7)
c(4,8)=c(3,7)+c(4,7)
我们把m-n当成一个线段,我们就可以让s(m,n) -> s(m+1,n),s(m-1,n),s(m,n+1),s(m,n-1);
我们可以进行O(1)转移,这里我们就好办了,我们用莫队离线排序查询然后进行转移
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define P 1000000007
#define mod 1000000007
#define N 100010
#define LL long long
using namespace std;
typedef long long ll;
struct query{int l,r,block,id;} Q[N];
LL w[N],inv[N],ans[N]; bool cmp(query a,query b)
{
if (a.block!=b.block) return a.l<b.l;
if (a.block&) return a.r<b.r; return a.r>b.r;
} LL C(int r,int l)
{
if (r<l) return ;
return w[r]*inv[l]%P*inv[r-l]%P;
} ll quick_pow(ll a,ll b){
ll ans=;
while(b){
if(b&) ans=(ans*a)%mod;
a=(a*a)%mod;
b=b/;
}
return ans;
}
int main()
{
w[]=;w[]=;inv[]=; inv[]=;
for (int i=;i<N;i++)
{
w[i]=w[i-]*i%P;
inv[i]=inv[P%i]*(P-P/i)%P;
}
for (int i=;i<N;i++) inv[i]=inv[i-]*inv[i]% P;
int block=sqrt();
int n;
cin>>n;
for (int i=;i<=n;i++)
{
cin>>Q[i].r>>Q[i].l;
Q[i].id=i;Q[i].block=Q[i].l/block;
}
int l=,r=;LL t=;
sort(Q+,Q+n+,cmp);
for (int i=;i<=n;i++)
{
while(l<Q[i].l)
{
t=(t+C(r,l+))%P;
l++;
}
while (l>Q[i].l)
{
l--;
t=(t-C(r,l+)+P)%P;
}
while (r<Q[i].r)
{
r++;
t=(*t-C(r-,l)+P)%P;
}
while(r>Q[i].r)
{
t=(t+C(r-,l))*inv[]%P;
r--;
}
ll sum=quick_pow((ll),Q[i].r);
sum=quick_pow(sum,mod-);
ans[Q[i].id]=(t*sum)%mod;
//ans[Q[i].id]=sum;
}
for (int i=;i<=n;i++)cout<<ans[i]<<endl;
return ;
}