杭二集训 disanti (51Nod 1667 概率好题)

标签:组合数,数学,容斥原理

题目

题目传送门

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

一个数,数据组数(T<=5)
对于每组数据 输入顺序为
k1 L1 R1…Lk1 Rk1
k2 L1 R1…Lk2 Rk2
(k1,k2<=8,1<=L<=R<=10^7)

Output

甲胜、平局、乙胜的概率。
(显然这个概率是有理数,记为p/q,则输出答案为(p/q)%(1e9+7))(逆元)

Input示例

1
1 1 2
1 1 4

Output示例

125000001 250000002 625000005

分析

我们考虑
将小O的区间[L,R]拆成[-inf,L-1]和[-inf,R]
将小Y的区间[L,R]拆成[R+1,inf]和[L,inf]
(inf是一个相对较大的数)
再用容斥 考虑相对差
对于赢 用k1+k2+1个数(>=0)去填补相对差
对于平 用k1+k2个数(>=0)去填补相对差
每次计算贡献用dfs即可

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define dep(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#define reg(x) for(int i=last[x];i;i=e[i].next)
using namespace std;
inline ll read()
{
    ll f=1,x=0;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;
}
const ll p=1e9+7;
ll n,m,l,r,sum,len[26],ans1,ans2,ans3,ni,T;
ll qpow(ll x,ll y){
    ll re=1;
    while(y){
        if(y&1)re=(re*x)%p;
        y>>=1;
        x=x*x%p;
    }
    return re;
}
ll C(ll m,ll n){
    if(m<n)return 0;
    ll ans=1;
    rep(i,1,n)ans=1LL*ans*(m-n+i)%p;
    rep(i,1,n)ans=1LL*ans*qpow(i,p-2)%p;
    return ans;
}
void dfs(int x,int y,int z){
    if(x>n+m){
        ans1=(ans1+y*C(sum-z+n+m-1,n+m))%p;ans1=(ans1+p)%p;
        ans2=(ans2+y*C(sum-z+n+m-1,n+m-1))%p;ans2=(ans2+p)%p;
        return;
    }
    dfs(x+1,y,z);dfs(x+1,-y,z+len[x]);
}
int main()
{
    T=read();
    while(T--){
        sum=0,ni=1,ans1=ans2=ans3=0;
        n=read();
        rep(i,1,n){
            l=read(),r=read();
            sum+=r,len[i]=r-l+1,ni=1LL*ni*len[i]%p;
        }
        m=read();
        rep(i,1,m){
            l=read(),r=read();
            sum-=l,len[i+n]=r-l+1,ni=1LL*ni*len[i+n]%p;
        }
        dfs(1,1,0);ans3=(ni-ans1-ans2)%p;ans3=(ans3+p)%p;ni=qpow(ni,p-2);
        ans1=1LL*ans1*ni%p;ans2=1LL*ans2*ni%p;ans3=1LL*ans3*ni%p;
        printf("%lld %lld %lld\n",ans1,ans2,ans3);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值