HDU-4790 Just Random (数形结合)

                               

                             


题意:给你a,b,c,d,p,m,随机等可能性的选择一对x,y,x∈[a,b],y∈[c,d],如果(x+y)%p=m,则称这次选择是成功的,问选择的成功率是多少。


思路:这道题有一种很好的理解方式,就是数形结合。我们可以把题目中的第一组数据理解为下面这幅图。


第一组数据里a=0,b=5,那么x的取值范围就是[0,5],c=0,d=5,那么y的取值范围也是[0,5],那么图中正方形内的所有点,包括边缘上的点,就是全部可能被选到的情况。并且每个点被选到的可能性相同。接下来我们再看p=3,m=0,那么(x+y)%p=m→→x+y=k*p+m→→x+y=3k,k为任意非负整数。所以图中的红线从左到右依次就对应k为0,1,2,3,4的情况。那么这些红线所经过的点就是满足x+y=3k的点。                                                

所以最终答案就是:正方形中红线经过的点的个数/正方形中全部点的个数

所以第一组数据的答案就是12/36→→1/3


那么接下来问题就转换成了如何才能快速求取红线经过的点的个数,我曾试过枚举每一个k,数据太大,果断T了,仔细观察后发现,可以分三种情况讨论,如下图。


首先我们要知道有一点,ab与cd互换对结果是没有任何影响的,因为图中每一条红线都关于y=x对称,将ab与cd互换后的图形也是关于y=x对称的,如图中的蓝色矩形和褐色矩形,所以我们可以假设(b-a)>(d-c)。

那么接下来就会有三种情况:

1,红线在①和②之间。因为每相邻两条红线之间相隔的距离是相等的,所以我们不难发现,在①和②之间的红线所经过的点呈等差数列,所以我们只需要找到最小的k1使得k1*p+m>=a+c(保证红线在红线①的右边或与①重合),和一个最大的k2使得k2*p+m<=a+d(保证红线在红线②的左边或与②重合),那么对于任意的k,k∈[k1,k2],都是满足这一种情况的k,之后利用等差数列求和的公式,可以得出:

红线所经过的点为 ((k1*p + m - a - c + 1) + (k2*p + m - a - c + 1))*(k2 - k1 + 1) / 2;

2,红线在②和③之间。同理,我们要找一个最小的k1和一个最大的k2使得对于任意的k,k∈[k1,k2],都是满足这一种情况的k,那么显而易见,k1就等于第一种情况的k2+1。那么k2就是要满足k2*p+m<=b+c。在这一段区间内的红线,每一条红线经过的点都是相同的,都是(d-c+1)。

红线所经过的点为 (k2-k1+1)*(d - c + 1);

3,红线在③和④之间。同理,k1=第二种情况的k2+1,k2满足k2*p+m<=b+d。这一段区间内的红线经过的点和第一种情况一样,呈等差数列。

红线所经过的点为((b + d - k1*p - m + 1) + (b + d - k2*p - m + 1))*(k2 - k1 + 1) / 2;


把这三种情况红线经过的点全部加起来,就是答案的分子,答案的分母就是矩形中的全部点(d - c + 1)*(b - a + 1);

然后用GCD约分一下就是最终答案了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <set>
#include <functional>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int INF = 1e9 + 5;
const int MAXN = 305;
const int MOD = 30021;
const double eps = 1e-8;
const double PI = acos(-1.0);

LL gcd(LL a, LL b){return b == 0 ? a : gcd(b, a%b);}

int main()
{
	int T;
	LL a, b, c, d, p, m,t,g;
	LL k1, k2;
	LL ans;
	LL aans;
	scanf("%d", &T);
	for (int CAS = 1; CAS <= T; CAS++)
	{
		ans = 0;
		scanf("%lld%lld%lld%lld%lld%lld", &a, &b, &c, &d, &p, &m);
		if ((d - c) > (b - a))
		{
			swap(a, c);
			swap(b, d);
		}
		if (a+c - m - 1 < 0)//第一种情况
			k1 = (a+c - m - 1) / p;
		else
			k1 = (a+c - m - 1) / p + 1;
		if (a + d - m < 0)
			k2 = (a + d - m) / p - 1;
		else
			k2 = (a + d - m) / p;
		ans += ((k1*p + m - a - c + 1) + (k2*p + m - a - c + 1))*(k2 - k1 + 1) / 2;

		k1 = k2 + 1;//第二种情况
		if (b + c - m < 0)
			k2 = (b + c - m) / p - 1;
		else
			k2 = (b + c - m) / p;
		ans += (k2-k1+1)*(d - c + 1);

		k1 = k2 + 1;//第三种情况
		if (b+d - m < 0)
			k2 = (b+d - m) / p - 1;
		else
			k2 = (b+d - m) / p;
		ans += ((b + d - k1*p - m + 1) + (b + d - k2*p - m + 1))*(k2 - k1 + 1) / 2;

		aans = (d - c + 1)*(b - a + 1);//总共的点
		g = gcd(ans, aans);//化简
		aans /= g;
		ans /= g;
		printf("Case #%d: %lld/%lld\n", CAS, ans, aans);
	}
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值