吉首大学2019年程序设计竞赛(重现赛)I 滑稽树上滑稽果 (莫队+逆元打表)
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
n个不同的滑稽果中,每个滑稽果可取可不取,从所有方案数中选取一种,求选取的方案中滑稽果个数不超过m的概率。(对109+7取模)
输入描述:
第一行一个正整数T( T <= 10^5 )随后T行每行两个整数n,m ( 0 < m <= n <= 10^5 )
输出描述:
T行,每行一个整数表示答案。
示例1
输入
复制
复制
2
5 2
5 1
5 2
5 1
输出
复制
复制
500000004
687500005
687500005
解题思路:很明显总方案数为2n种,而合法的方案数为C(n,0)+C(n,1)+……+C(n,m)种
因为有t组询问,如果每次暴力求,必然超时。
我们令S(n,m)=C(n,0)+C(n,1)+……+C(n,m),答案就是S(n,m)/2n
既然暴力超时,我们考虑离线的做法,我们会很自然想到莫队,但是要用莫队,我们必须要满足在在O(1)的时间下得到[L,R-1]和[L,R+1]和[L-1,R]和[L+1,R]的答案.
首先来看m的变化,很简单S(n,m)=S (n,m-1)+C(n,m)
然后来看n的变化,由于C(a,b)=C(a-1,b)+C(a-1,b-1),所以我们可以得到S(n,m)=2S(n-1,m)-C(n-1,m)
然后直接上莫队就好了。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; const int N=1e5+7; int n,m,pos[N]; long long re[N],inv[N],fac[N]; struct node{ int l,r,id; }Q[N]; ll Ans=2,ans[N]; int L=1,R=1; ll qpow(ll a,ll b){ ll res=1; while(b){ if(b&1) res=res*a%mod; b>>=1; a=a*a%mod; } return res; } bool cmp(node x,node y){ if(pos[x.l]==pos[y.l]) return pos[x.r]<pos[y.r]; return pos[x.l]<pos[y.l]; } void init(int n){ re[0] = inv[1] = fac[0] = 1; for(int i = 1;i <= n;++i) fac[i] = fac[i-1] * i % mod; for(int i = 2;i <= n;++i) inv[i] = (mod-mod/i)*inv[mod%i] % mod; for(int i = 1;i <= n;++i) re[i] = re[i-1] * inv[i] % mod; } long long C(int a,int b){ if(a < 0||b>a) return 0; return fac[a]*re[b]%mod*re[a-b]%mod; } void addL(int l,int r){ Ans=(Ans+C(r,l))%mod; } void delL(int l,int r){ Ans=(Ans-C(r,l)+mod)%mod; } void addR(int l,int r){ Ans=(2*Ans-C(r-1,l)+mod)%mod; } void delR(int l,int r){ Ans=(Ans+C(r-1,l))%mod*inv[2]%mod; } int main(){ init(100000); int t; scanf("%d",&t); int sz=sqrt(100000); for(int i=1;i<=100000;i++) pos[i]=(i-1)/sz+1; for(int i=1;i<=t;i++){ scanf("%d%d",&Q[i].r,&Q[i].l); Q[i].id=i; } sort(Q+1,Q+t+1,cmp); for(int i=1;i<=t;i++){ while(L<Q[i].l){ L++; addL(L,R); } while(L>Q[i].l){ delL(L,R); L--; } while(R<Q[i].r){ R++; addR(L,R); } while(R>Q[i].r){ delR(L,R); R--; } ans[Q[i].id]=Ans*qpow(qpow(2,R),mod-2)%mod; } for(int i=1;i<=t;i++) printf("%lld\n",ans[i]); return 0; }