• I 滑稽树上滑稽果(莫队+组合数)


    链接:https://ac.nowcoder.com/acm/contest/992/I
    来源:牛客网

    题目描述

    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


    思路:每个果子有取与不取两种能,所以有2^n种可能,而不超过取m个的概率是 C(n,m)+C(n,m-1)+...+C(n,0)
    这里令 S(n,m)= C(n,m)+C(n,m-1)+...+C(n,0);答案就是 S(n,m)/2^n % mod
    暴力写是会超时的,因为测试样例 T<=1e5,n<=1e5.

    很明显测试样例太多,所以要采用离线的算法,就会想到莫队。那就要求知道小于和大于n,m时的关系
    因为:
    S(n,m)=C(n,0)+C(n,1)+...+C(n,m-1) +C(n,m)=S(n,m-1)+C(n,m) 这样就可以得到大于小于m的转换关系

    因为: C(n,m) = C(n-1,m)+C(n-1,m-1)
        C(n,m-1) = C(n-1,m-1)+C(n-1,m-2)
    .... ....
        C(n,1) = C(n-1,0)+C(n-1,1)
        C(n,0) = C(n-1,0) ==1
    上式叠加得 S(n,m)=2*S(n-1,m)-C(n-1,m) 这样就可以的到大于小于n的转化关系。
    当然期间有除法再去摸,明显要用逆元,这里用的是费马小定理
    代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+10;
    const int mod=1e9+7;
    struct node{
        int n,m,id;
    }a[maxn];
    ll f[maxn],nif[maxn],zu[maxn],ans[maxn];
    
    ll q_pow(ll a,ll b)//快速幂,用于费马小定理 
    {
        ll ans=1;
        while(b)
        {
            if(b&1)
                ans=(ans*a)%mod;
            a=(a*a)%mod;
            b>>=1;
        }
        return ans;
    } 
    void init()
    {
        f[1]=1;
        int black=sqrt(maxn);
        for(int i=2;i<maxn;i++)//阶乘 
            f[i]=(f[i-1]*i)%mod;
        for(int i=1;i<maxn;i++)
            zu[i]=(i-1)/black+1;
        for(int i=1;i<maxn;i++)//阶乘的逆元 
            nif[i]=q_pow(f[i],mod-2);
    }
    bool cmp(node a1,node a2)
    {
        if(zu[a1.n]!=zu[a2.n])
            return zu[a1.n]<zu[a2.n];
        return a1.m<a2.m;
    }
    ll cal(ll n,ll m)//C(n,m) 
    {
        if(m>n||n==0||n<0) return 0;
        if(m==n||m==0) return 1;
        return ((f[n]*nif[m])%mod*nif[n-m])%mod;
    }
    int main()
    {
        init();
        int t;
        scanf("%d",&t); 
        for(int i=1;i<=t;i++)
        {
            scanf("%d%d",&a[i].n,&a[i].m);
            a[i].id=i;
        }
        sort(a+1,a+t+1,cmp);
        ll N=1,M=1,sum=2;//s(1,1)=2 
        for(int i=1;i<=t;i++)
        {
            while(N<a[i].n)  sum=(2*sum-cal(N++,M)+mod)%mod;
            while(N>a[i].n)  sum=((sum+cal(--N,M))%mod*nif[2])%mod;
            while(M<a[i].m)  sum=(sum+cal(N,++M))%mod;
            while(M>a[i].m)  sum=(sum-cal(N,M--)+mod)%mod;
            ans[a[i].id]=(sum*q_pow(q_pow(2,a[i].n),mod-2))%mod;
        }
        for(int i=1;i<=t;i++)
            printf("%lld
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    『软件介绍』SQLServer2008 基本操作
    PCA的数学原理
    PCA的数学原理
    Oracle数据处理
    UVa 11995
    Unreal Engine 4 C++ 为编辑器中Actor创建自己定义图标
    codecombat之边远地区的森林1-11关及地牢38关代码分享
    初识ecside
    how tomcat works读书笔记 七 日志记录器
    HDU 1754(线段树区间最值)
  • 原文地址:https://www.cnblogs.com/xiongtao/p/11190914.html
Copyright © 2020-2023  润新知