[51nod 1667]概率好题

Description

甲乙两人在van游戏。
他们各有k1,k2个集合[li,ri]
每个人可以从各自的每个集合中随机选出一个整数。
S1= S2=
若S1>S2甲胜,S1=S2平局,S1< S2乙胜。
求三种情况各自的概率,答案对10^9+7取模(逆元)
k1,k2<=8,li<=ri<=10^7

Solution

看到k1k2辣么小显然是一种叫做容斥的方法啦
但是发现有上界也有下界的限制还有加有减不好做,我们可以转换一下模型。
我们可以把甲选的每个数表示成 rixi ,把乙选的每个数表示成 li+xi (0 < xi < |i|)
那么甲胜的情况就是

(rixi)>(li+xi)

(rixi)(li+xi)>0

rilixiyi>0

xi+yi<rili

咦?右边是一个常数!!!
那么我们可以写成 xi+yi+z=sum 的形式(0<=k<=+∞)
其中 sum=rili1
-1是因为前面是小于号,不带等于。
那么这样就变成了一堆有上限的数的和等于一个常数的套路了。。。
直接隔板法就好了。。。
这个地方k1k2比较小,组合数可以直接暴力求。。。
然后就没有了。。。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int mo=1e9+7;
int ty,n,m,l,r,sum,len[17],ans1,ans2,ans3,ni;
int mi(int x,int y) {
    int z=1;
    for(;y;y/=2,x=(ll)x*x%mo) if (y&1) z=(ll)z*x%mo;
    return z;
}
int c(int m,int n) {
    if (m<n) return 0;
    int ans=1;
    fo(i,1,n) ans=(ll)ans*(m-n+i)%mo;
    fo(i,1,n) ans=(ll)ans*mi(i,mo-2)%mo;
    return ans;
}
void dfs(int x,int y,int z) {
    if (x>n+m) {
        (ans1+=y*c(sum-z+n+m-1,n+m))%=mo;(ans1+=mo)%=mo;
        (ans2+=y*c(sum-z+n+m-1,n+m-1))%=mo;(ans2+=mo)%=mo;
        return;
    }
    dfs(x+1,y,z);
    dfs(x+1,-y,z+len[x]);
}
int main() {
    for(scanf("%d",&ty);ty;ty--) {
        sum=0;ni=1;ans1=ans2=ans3=0;
        scanf("%d",&n);fo(i,1,n) scanf("%d%d",&l,&r),sum+=r,len[i]=r-l+1,ni=(ll)ni*len[i]%mo;
        scanf("%d",&m);fo(i,1,m) scanf("%d%d",&l,&r),sum-=l,len[i+n]=r-l+1,ni=(ll)ni*len[i+n]%mo;
        dfs(1,1,0);ans3=(ni-ans1-ans2)%mo;(ans3+=mo)%=mo;ni=mi(ni,mo-2);
        ans1=(ll)ans1*ni%mo;ans2=(ll)ans2*ni%mo;ans3=(ll)ans3*ni%mo;
        printf("%d %d %d\n",ans1,ans2,ans3);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值