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辣么小显然是一种叫做容斥的方法啦
但是发现有上界也有下界的限制还有加有减不好做,我们可以转换一下模型。
我们可以把甲选的每个数表示成
ri−xi
,把乙选的每个数表示成
li+xi
(0 < xi < |i|)
那么甲胜的情况就是
∑(ri−xi)>∑(li+xi)
∑(ri−xi)−∑(li+xi)>0
∑ri−∑li−∑xi−∑yi>0
∑xi+∑yi<∑ri−∑li
咦?右边是一个常数!!!
那么我们可以写成 ∑xi+∑yi+z=sum 的形式(0<=k<=+∞)
其中 sum=∑ri−∑li−1
-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);
}
}