51nod1667 概率好题
一个数,数据组数(T<=5) 对于每组数据 输入顺序为 k1 L1 R1...Lk1 Rk1 k2 L1 R1...Lk2 Rk2 (k1,k2<=8,1<=L<=R<=10^7)
甲胜、平局、乙胜的概率。 (显然这个概率是有理数,记为p/q,则输出答案为(p/q)%(1e9+7))(逆元)
1 1 1 2 1 1 4
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\n",smm-x+n-1,n); 54 ans2=((LL)ans2+f*C(smm-x+n-1,n-1))%mod; 55 // printf("%d\n",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\n",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$的乘积