题意:
从区间 [ a , b ] 中间选择一个数 x ,从区间 [ c , d ] 中间选择一个数 y ,使得( x + y )% p == m 问几率有多大。
输出按照最简分式;
分析:
直接在这两个区间中间选择数不好选,那么可以算区间 [ 0 , b ] 跟区间 [ 0 , d ] 中间有多少满足的,然后容斥以下。
设 f ( a , b ) 表示的是在区间0~a和区间0~b中分别取x和y使得(x+y)%p==m的事件数。
那么答案可以写为 ans = f ( b ,d ) - f ( b , c-1 ) - f ( a-1 , d ) + f ( a-1 , c-1 )
例如:计算f(16,8),p=6,m=2.
因为(x+y)%p=(x%p+y%p)%p.所以重写一下0~a和0~b两个区间。
对于a有:0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4
对于b有:0 1 2 3 4 5 0 1 2
a中(0 1 2 3 4 5)的数量为 a/p个,b中(0 1 2 3 4 5)的数量为 b/p个。
所以根据这个将两个区间分为四部分。
这样取A集合为(0 1 2 3 4 5/0 1 2 3 4 5),B集合为(0 1 2 3 4)
C集合为(0 1 2 3 4 5/),D集合为(0 1 2)。
这样就可以分成4部分来计算了。
f(16,8)=A和C满足条件的数+A和D满足条件的数+B和C满足条件的数+B和D满足条件的数。
一、A和C满足条件的数:
A中先取一个其中一个(0 1 2 3 4 5),C中取一个(0 1 2 3 4 5)从A中取一个数x,C中任意一个数y相加对p取余的范围是[0~5]也就是说 一个x对应一个y,A中有a/p个(0 1 2 3 4 5)C中有b/p个(0 1 2 3 4 5)每个区间p个数,所以总数为ans=(a/p)*(b/p)*p.
二、A和D满足条件的数:
D集合中元素数目mb=b%p+1(元素0)。根据( 一、),AD满足的条件数为ans+=mb*(a/p)。
三、B和C满足条件的数:
跟(二、)同理,为ans+=(a%p+1)*(b/p)。
四、B和D满足条件的数:
首先令:
ma=a%p
mb=b%p
用ma进行分类,ma种的数跟mb中的数对应相加。
(1)若 ma>m
则 ans += min ( mb , m )+1 因为若有 ma>m 则此时肯定有 m +1个满足 ,0+m=m 1+m-1=m 依次类推。
然而可能存在mb小于m,所以两者取一个最小值。然而这并不是最终结果,因为还可能存在 (ma+mb)%p>=m,可以看出来,当mb小于等于m的时候无论如何都取不到那种情况,当mb>m的时候才能取到,令t=(m-ma+p)%p,此时t代表满足上面条件的最小值,即(ma+t)%p>=m,t为满足条件最小值。并且t一定大于m,所以不存在与上面产生交集,判断如果t<=mb,那么还有mb-t+1个数满足条件。
(2)若ma<=m
因为不存在(1)中的第一种情况,所以直接考虑第二种情况,令t=(m-ma+p)%p,此时t一定小于m,如果t<=mb,则说明D中有与之相对应的。
那么一共有mb-t+1个?,不对,可能多算,因为如果mb>m的话,可能多算,因为0+m=m,1+m-1=m....ma+t=m,正好。
所以再加上min(m-t+1,mb-t+1)
#include
using namespace std;
typedef long long ll;
ll a,b,c,d,p,m;
ll f(ll a,ll b){
if(a<0||b<0) return 0;
ll ans=(a/p)*(b/p)*p;
ll ma=a%p,mb=b%p;
ans+=(b/p)*(ma+1);
ans+=(a/p)*(mb+1);
if(ma>m){
ans+=min(mb,m)+1;
ll t=(m-ma+p)%p;
if(t<=mb) ans+=mb-t+1;
}else{
ll t=(m-ma+p)%p;
if(t<=mb) ans+=min(m-t+1,mb-t+1);
}
return ans;
}
int main(){
int T;
scanf("%d",&T);
for(int cs=1;cs<=T;cs++){
scanf("%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&p,&m);
ll ans=f(b,d)-f(b,c-1)-f(a-1,d)+f(a-1,c-1);
ll sum=(b-a+1)*(d-c+1);
ll t=__gcd(ans,sum);
sum/=t;ans/=t;
printf("Case #%d: %lld/%lld\n",cs,ans,sum);
}
return 0;
}