51nod1667 概率好题

基准时间限制:4 秒 空间限制:131072 KB 分值: 640 
甲乙进行比赛。
他们各有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

数学问题 容斥

$[L_i,R_i]$的限制看上去很迷,不怎么好做。

如果能去掉下界的话,原问题似乎可以转化成容斥求方程解的个数的问题。

我们来试试去掉下界:

设前ki个集合为 $R_i - x_i$,后ki个集合为 $ L_i + x_i $

此时x的取值范围是 $[0,R_i - L_i]$

那么甲赢乙的情况需要满足的条件是:

$$sum_{i=1}^{k_1} R_i-x_i > sum_{j=1}^{k_2} L_j+y_j $$

$$sum_{i=1}^{k_1} x_i + sum_{j=1}^{k_2} y_j< sum_{i=1}^{k_1} R_i -sum_{j=1}^{k_2} L_j $$

我们惊喜地发现右边是常数,那么可以用组合数+容斥算方程解的个数辣

甲乙平手的情况,只需要把上面的大于换成等于号即可。

乙赢甲的情况,可以把上式取负计算解个数,也可以直接用总方案数减去前两问方案数。

总方案数当然就是所有的$R_i-L_i+1$的乘积

答案当然就是满足条件的方案数除以总方案数 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 #define LL long long
 7 using namespace std;
 8 const int mod=1e9+7;
 9 const int mxn=100001;
10 int read(){
11     int x=0,f=1;char ch=getchar();
12     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
13     while(ch>='0' && ch<='9'){x=x*10-'0'+ch;ch=getchar();}
14     return x*f;
15 }
16 int ksm(int a,int k){
17     int res=1;
18     while(k){
19         if(k&1)res=(LL)res*a%mod;
20         a=(LL)a*a%mod;
21         k>>=1;
22     }
23     return res;
24 }
25 int fac[mxn*100],inv[mxn*100];
26 void init(){
27     int ed=mxn*100;
28     fac[0]=fac[1]=1;inv[0]=inv[1]=1;
29     for(int i=2;i<ed;i++){
30         fac[i]=(LL)fac[i-1]*i%mod;
31         inv[i]=((-mod/i*(LL)inv[mod%i]%mod)+mod)%mod;
32     }
33     return;
34 }
35 int C(int n,int m){
36     if(m>n || n<0)return 0;
37 //    return (LL)fac[n]*inv[m]%mod*inv[n-m]%mod;
38     int res=1;
39     for(int i=1;i<=m;i++){
40         res=(LL)res*(n-m+i)%mod;
41     }
42     for(int i=1;i<=m;i++){
43         res=(LL)res*ksm(i,mod-2)%mod;
44     }
45     return res;
46 }
47 int ans1,ans2,ans3;//1 2 0
48 int n,smm,lower=0;
49 int k1,k2,L[mxn],R[mxn];
50 void calc(int pos,int f,int x){
51     if(pos>n){
52         ans1=((LL)ans1+f*C(smm-x+n-1,n))%mod;
53 //        printf("%d %d
",smm-x+n-1,n);
54         ans2=((LL)ans2+f*C(smm-x+n-1,n-1))%mod;
55 //        printf("%d
",ans1);
56         return;
57     }
58     calc(pos+1,-f,x+R[pos]-L[pos]+1);
59     calc(pos+1,f,x);
60     return;
61 }
62 int main(){
63     int i,j;
64 //    init();
65     int T=read();
66     while(T--){
67         ans1=ans2=ans3=0;
68         lower=1;smm=0;
69         k1=read();
70         for(i=1;i<=k1;i++){
71             L[i]=read();R[i]=read();
72             smm+=R[i];
73         }
74         k2=read();
75         for(i=1;i<=k2;i++){
76             L[i+k1]=read();R[i+k1]=read();
77             smm-=L[i+k1];
78         }
79         n=k1+k2;
80         for(i=1;i<=n;i++)lower=(LL)lower*(R[i]-L[i]+1)%mod;
81         calc(1,1,0);
82         int INV=ksm(lower,mod-2);
83         ans3=((LL)lower-ans1-ans2)*INV%mod;
84         ans1=(LL)ans1*INV%mod;
85         ans2=(LL)ans2*INV%mod;
86         ans1=(ans1+mod)%mod;
87         ans2=(ans2+mod)%mod;
88         ans3=(ans3+mod)%mod;
89         printf("%d %d %d
",ans1,ans2,ans3);
90     }
91     return 0;
92 }

设前ki个集合为 $R_i - x_i$,后ki个集合为 $ L_i + x_i $此时x的取值范围是 $[0,R_i - L_i]$那么甲赢乙的情况需要满足的条件是:$$sum_{i=1}^{k_1} R_i-x_i > sum_{j=1}^{k_2} L_j+y_j $$$$sum_{i=1}^{k_1} x_i + sum_{j=1}^{k_2} y_j< sum_{i=1}^{k_1} R_i -sum_{j=1}^{k_2} L_j $$我们惊喜地发现右边是常数,那么可以用组合数+容斥算方程解的个数辣甲乙平手的情况,只需要把上面的大于换成等于号即可。乙赢甲的情况,可以把上式取负计算解个数,也可以直接用总方案数减去前两问方案数。总方案数当然就是所有的$R_i-L_i+1$的乘积

原文地址:https://www.cnblogs.com/SilverNebula/p/7057643.html