51nod 1667 概率好题

Problem

甲乙进行比赛。
他们各有 k1,k2 个集合 [Li,Ri]
每次随机从他们拥有的每个集合中都取出一个数
S1= 甲取出的数
S2 同理
S1>S2 甲胜 若 S1=S2 平局 否则乙胜
分别求出甲胜、平局、乙胜的概率。

Solution

对于甲的每个数可以表示为这样一个形式 Rixi 其中 xi[0,RiLi]
类似的,对于乙中的每个数可以表示为这样一个形式 Li+yi 其中 yi[0,RiLi]
那么甲胜利的条件即为:
Rixi>Li+yi
变形为:
xi+yi<RiLi
于是可以发现右边的是一个常数
我们加入一个变量 k(k[0,+)
那么不等式变成:
xi+yi+k=RiLi1
(注意有一个-1是因为不等式中是<)
于是就变成了求方程有多少个不同的解。

容斥

问题现在变成:
我们有一个有 k1+k2+1 个未知数的方程,其中每个未知数都是非负整数且有上限,现在我们需要知道有多少组解。
于是我们可以容斥,答案要求的是满足所有未知数都在其上限以内的方案数,于是设 S(i) 表示至少有i个未知数不符合要求的方案数,那么可以得到:
Ans=(1)i×S(i)
枚举未知数符合或不符合的状态,那么对于一个不符合要求的未知数,由于原本应当是 xiupi ,不符合就变成 xi>upi ,即 xiupi10
所以将等式的常数减去 upi+1 就变成了经典的挡板问题:将n个球放进m个盒子里,求方案数。(注意:由于此问题中m很小,于是可以直接暴力算组合数)

记第一道640

这里写图片描述

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)

using namespace std;

int get(){
    char ch;
    int s=0;
    bool bz=0;
    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
    if (ch=='-')bz=1;else s=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
    if (bz)return -s;
    return s;
}

const int N = 20;
const int mo = 1e+9+7;

typedef long long LL;

int u[N];
int n,k1,k2,t;
LL sum,ans;
bool bz[N];

LL quickmi(LL x,LL tim){
    LL ans=1;
    while(tim){
        if (tim%2)ans=ans*x%mo;
        x=x*x%mo;
        tim/=2;
    }
    return ans;
}

LL c(LL n,int m){
    LL ans=1;
    fo(i,1,m)ans=ans*(n-i+1)%mo;
    fo(i,1,m)ans=ans*quickmi(i,mo-2)%mo;
    return ans;
}

void calc(){
    LL tmp=sum;
    fo(i,1,n)
    if (bz[i])tmp-=u[i]+1;
    if (tmp<0)return;
    LL v=c(tmp+n,n);
    bool pd=0;
    fo(i,1,n)pd^=bz[i];
    if (pd)ans=(ans+mo-v)%mo;
    else ans=(ans+v)%mo;
}

void getans(int x){
    if (x>n){
        calc();
        return;
    }
    bz[x]=0;
    getans(x+1);
    bz[x]=1;
    getans(x+1);
}

int main(){
    t=get();
    fo(cas,1,t){
        k1=get();
        sum=0;
        fo(i,1,k1){
            int l=get(),r=get();
            u[i]=r-l;
            sum+=r;
        }
        k2=get();
        fo(i,1,k2){
            int l=get(),r=get();
            u[i+k1]=r-l;
            sum-=l;
        }
        n=k1+k2;
        u[n+1]=1e+9;
        sum--;
        LL ans1=0;ans=0;
        if(sum<0)ans1=0;
        else{
            getans(1);
            ans1=ans;
        }
        ans=0;
        sum++;
        if (sum<0)ans=0;
        else getans(1);
        ans=(ans-ans1+mo)%mo;
        fo(i,1,n)ans=ans*quickmi(u[i]+1,mo-2)%mo;
        fo(i,1,n)ans1=ans1*quickmi(u[i]+1,mo-2)%mo;
        printf("%lld %lld %lld\n",ans1,ans,(1+mo*2-ans-ans1)%mo);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值