吉首大学2019年程序设计竞赛(重现赛)I 滑稽树上滑稽果 (莫队+逆元打表)

时间限制:C/C++ 1秒,其他语言2秒
空间限制: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
 
输出
复制
500000004
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;
}

 

posted @ 2019-07-28 18:51  两点够吗  阅读(198)  评论(0编辑  收藏  举报