HDU4790_Just Random

  这个题目我只能说我一看就知道是这么做的,但是由于实现能力略水,Wa了3发。

题意为给你两个区间[a,b]和[c,d],两个区间分别任取一个数,现在要你求出这个数模p的值为m的概率有多大。

其实是这么做的,我们需要统计总共有多少种组合情况然后等于就知道分母和分子了。

但是怎么求种类数呢 ???

首先我们定义一个函数count(x,y),表示从0-x,0-y随机取两数满足情况的种类数。

那么最后我们要求的答案ans=count(a,b)-count(a-1,b)-count(a,b-1)+count(a-1,b-1)。

这是容斥原理,到这里问题就转化为了求函数值呢。

但是我们的根本的问题还是没有解决。到底怎么求呢?

我们可以这样考虑,给一个数A,现在问你x%p=m(x不大于A)的种类数有多少?

显然,如果A%p>=m,答案就是A/p+1,如果A%p<m,答案就是A/p。

这里自己算一算就知道了。。。

然后我们等于是由二维优化到了一维,但是还是不够,还有继续优化。

我们知道取模具有周期性,所以我们可以保证最后我们要求其中一个的范围的不会超过p。

假设我们从0开始循环枚举第一个i,那么每次i没增加1,其实就是最终的种类数相当于m减1,如果我们看成p个数对应的函数值,那么我们等于是把每一个函数值右移了一位。

如果你理解了上面,那么恭喜你,可以A掉这个题目了。

具体来说就是这样的,首先不超过A%p的函数值都为A/p+1,超过的部分为A/p。也就是说,只有两种不同的函数值,而且是连续的。

接下来我们等于是每次移动一位,然后求某一固定位置的函数值,这样我们发现移动若干位后的函数值才会变化一次,而且总共最多变化两次(因为循环部分的可以直接计算了),所以找出突变点做一次乘法就可以出答案了。

其实函数移动就是求连续段的函数值啦。。。只是那样好理解一定。

手滑Wa了若干发。。。。OTL。代码也挺挫的,求见谅。

哦,对了,是循环移动。 

 

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;

ll a,b,c,d,n,m,p,ans,t,cas=0,G,sum;

ll gcd(ll A,ll B) { return B==0?A:gcd(B,A%B); }

ll count(ll x,ll y)
{
    if (x<0 || y<0) return 0;
    ll tot=0,flag=y%p,left=(x+1)%p,cur=m,End=(cur-left+1+p)%p;
    ll all=((y%p+1)*(y/p+1)+(p-1-y%p)*(y/p));
    tot=((x+1)/p)*all;  
    if (left==0) return tot;
    if (cur<End && End<=flag)
    {
        tot+=all-(End-cur-1)*(y/p+1);
        return tot;
    }
    if (cur<End && cur>flag)
    {
        tot+=all-(End-cur-1)*(y/p);
        return tot;
    }
    if (cur<End)
    {
        tot+=(cur+1)*(y/p+1)+(p-End)*(y/p);
        return tot;
    }
    if (cur>=End && End>flag)
    {
        tot+=(cur-End+1)*(y/p);
        return tot;
    }
    if (cur>=End && cur<=flag)
    {
        tot+=(cur-End+1)*(y/p+1);
        return tot;
    }
    if (cur>End)
    {
        tot+=(cur-flag)*(y/p)+(flag-End+1)*(y/p+1);
        return tot;
    }
    return tot;
}

int main()
{
    scanf("%I64d",&t);
    while (t--)
    {
        scanf("%I64d%I64d%I64d%I64d%I64d%I64d",&a,&b,&c,&d,&p,&m);
        ans=count(b,d)-count(a-1,d)-count(b,c-1)+count(a-1,c-1);
        sum=(b-a+1)*(d-c+1);
        if (ans==0)
        {
            printf("Case #%I64d: 0/1\n",++cas);
            continue;
        }
        G=gcd(ans,sum);
        ans/=G,sum/=G;
        printf("Case #%I64d: %I64d/%I64d\n",++cas,ans,sum);
    }
    return 0;
}

 

 

 

posted @ 2013-11-16 17:40  092000  阅读(562)  评论(0编辑  收藏  举报