51nod 1667 概率好题 组合数学+容斥原理

本文介绍了一个比赛胜负概率的计算问题,甲乙双方各自拥有多个数值范围,在每轮比赛中随机抽取数值并比较总和来决定胜负。通过组合数学方法计算甲胜、平局及乙胜的概率,并使用快速幂和逆元技巧进行高效求解。

题意

甲乙进行比赛。
他们各有k1,k2个集合[Li,Ri]
每次随机从他们拥有的每个集合中都取出一个数
S1=sigma甲取出的数,S2同理
若S1>S2甲胜 若S1=S2平局 否则乙胜
分别求出甲胜、平局、乙胜的概率。
(显然这个概率是有理数,记为p/q,则输出答案为(p/q)%(1e9+7))(逆元)
注意 多组数据
T<=5,k1,k2<=8,1<=L<=R<=10^7。

分析

%ymw

代码

#include <bits/stdc++.h>

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

typedef long long ll;

const int N = 10;
const int MOD = 1000000007;

int n,m,ans;

int lim1[N],lim2[N];

int l1[N],r1[N],l2[N],r2[N];

int ny[25];

int ksm(int x,int y)
{
    int res = 1;
    while (y)
    {
        if (y & 1)
            res = 1ll * x * res % MOD;
        x = 1ll * x * x % MOD;
        y >>= 1;
    }
    return res;
}

int getC(int n,int m)
{
    int ans = 1;
    for (int i = 0; i < m; i++)
        ans = 1ll * ans * (n - i) % MOD;
    ans = 1ll * ans * ny[m] % MOD;
    return ans;
}

void dfs(int x,int y,int s,int f)
{
    if (x > n + m)
    {
        if (s < 0)
            return;
        if (y & 1)
        {
            ans += MOD - getC(s + n + m + f - 1, n + m + f - 1);
        }
        else
        {
            ans += getC(s + n + m + f - 1, n + m + f - 1);
        }
        ans -= ans > MOD ? MOD : 0;
        return;
    }
    dfs(x + 1, y, s, f);
    if (x <= n)
        dfs(x + 1, y + 1, s - lim1[x] - 1, f);
    else
        dfs(x + 1, y + 1, s - lim2[x - n] - 1, f);
}

void pre()
{
    ny[0] = ny[1] = 1;
    for (int i = 2; i <= 20; i++)
        ny[i] = 1ll * (MOD - MOD / i) * ny[MOD % i] % MOD;
    for (int i = 2; i <= 20; i++)
        ny[i] = 1ll * ny[i - 1] * ny[i] % MOD;
}

int main()
{
    pre();
    int T = read();
    while (T--)
    {
        scanf("%d",&n);
        int tot = 1;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d%d",&l1[i],&r1[i]);
            lim1[i] = r1[i] - l1[i];
            tot = 1ll * tot * (r1[i] - l1[i] + 1) % MOD;
        }
        scanf("%d",&m);
        for (int i = 1; i <= m; i++)
        {
            scanf("%d%d",&l2[i],&r2[i]);
            lim2[i] = r2[i] - l2[i];
            tot = 1ll * tot * (r2[i] - l2[i] + 1) % MOD;
        }
        tot = ksm(tot,MOD - 2);

        int s = -1,a[3];
        for (int i = 1; i <= n; i++)
            s -= l1[i];
        for (int i = 1; i <= m; i++)
            s += r2[i];
        ans = 0;
        dfs(1,0,s,1);
        a[2] = 1ll * ans * tot % MOD;

        ans = 0;
        dfs(1,0,s + 1,0);
        a[1] = 1ll * ans * tot % MOD;

        for (int i = 1; i <= std::max(n,m); i++)
            std::swap(l1[i],l2[i]), std::swap(r1[i],r2[i]), std::swap(lim1[i],lim2[i]);
        std::swap(n,m);
        s = -1;
        for (int i = 1; i <= n; i++)
            s -= l1[i];
        for (int i = 1; i <= m; i++)
            s += r2[i];
        ans = 0;
        dfs(1,0,s,1);
        a[0] = 1ll * ans * tot % MOD;

        printf("%d %d %d\n",a[0],a[1],a[2]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值