HDU4790 Just Random

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4790
训练的时候被这题坑惨了啊,看题解也看了半天才懂,所以准备详细的写一个题解,以便以后复习。
题目意思是:从区间[a,b]选取一个数字x,区间[c,d]选取一个数字y,使得(x+y)%p=m。问你几率有多大。最简分式输出(除gcd就可以)。
总的事件总数就是(b-a+1)*(d-c+1)。
关键是求 满足(x+y)%p=m 的选择方法总数。
如何求呢?
其实直接求不好求,而且容易超时。
我们可以先利用容斥原理转化一下。
假定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(a-1,c-1)是被减了两遍的)。
那如何求f(a,b)呢,其实这是关键。
下面我引用一个博客的例子:
例如:求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,7)=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满足条件的数:
最难求的就是这个啦,难求在哪呢,或者说跟前面的区别在哪呢。
因为B或D中任意一元素并不一定在另一个集合中有对应,例如B中 (3,4,5)在D中都没有对应。所以我们这样分情况:
(1)ma>m:(B集合元素:0,1,2……m……ma)
ma大于m就意味着集合D中最多有m+1个元素(元素0)跟他相对应,为什么最多呢,因为D集合中元素个数可能小于m+1。所以ans+=min(m,mb)+1。这是所有情况吗?其实不是,当(mb+ma)%p>=m时就会漏掉一些情况。D集合中最大元素为mb,B中最大为ma,令t=(m-ma+p)%p(如果mb小于m那么t>mb?)。这是求出来的满足B中最大项在D对应的值,如果t<=mb那就说明D中有mb-t+1个元素(区间t~mb)与之相对应。ans+=mb-t+1。
(2)ma<=m:(B集合元素:0,1…ma)
ma<=m时,D集合中最多有ma个元素跟他相对应啦。所以跟(1)后半部分相似。D集合中最大元素为mb,B中最大为ma,同样令t=(m-ma+p)%p。也就是ma对应的D集合中最大值。如果t<=mb那就说明D中有元素跟他对应。 假设有k个,首先B集合中肯定有0没0就说明B集合没有元素。D中跟0相对应的是m,所以元素个数不会超过k=m-t+1个。mb可能小于m吧,这个之前并没有讨论。所以k也不会超过mb-t+1个。ans+=min(mb-t+1,m-t+1)。
至此,所有情况就讨论完了。
代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll __int64
ll p,m;
ll gcd(ll a,ll b)
{
    if(awhile(b){
        ll t=a;
        a=b;
        b=t%b;
    }
    return a;
}
ll f(ll a,ll b)
{
    if(a<0||b<0) 
        return 0;
    ll ma=a%p,mb=b%p,ans;
    ans=(a/p)*(b/p)*p;
    ans+=(ma+1)*(b/p)+(mb+1)*(a/p);
    if(ma>m){
        ans+=min(m,mb)+1;
        ll t=(m-ma+p)%p;
        if(t<=mb) ans+=mb-t+1;
    }else{
        ll t=(p+m-ma)%p;
        if(t<=mb) ans+=min(m-t+1,mb-t+1);
    }
    return ans;
}
int main()
{
    int ca=0,t;
    ll a,b,c,d;
    scanf("%d",&t);
    while(t--){
        cin>>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 tot=(b-a+1)*(d-c+1);
        ll g=gcd(ans,tot);
        printf("Case #%d: %I64d/%I64d\n",++ca,ans/g,tot/g);
    }
    return 0;
}

你可能感兴趣的:(HDU4790,题解)