题目链接:https://cn.vjudge.net/problem/HDU-4790
题意:给你两个区间[a, b], [c, d],从[a, b]里等可能的选出一个数x, 从[c, d]里等可能的选出一个数y,求 使得(x + y) % p = m 成立的的所有(x ,y)的比例, 如果个数为零 输出0/1 否则输出 (x, y) / sum 的最简形式
根据这样打表可以看出 满足情况的个数是 先单调递增,再不变,再单调递减
分别讨论等差数列的首项和项数。
可以发现满足条件的每一列都相差 p
第一个等差数列的首项的位置和大小总是 m+1
所以求项数 n利用 an = a1 + (n - 1) *d
再求中间不变的部分的首项(如果存在的话),当前的 a1 = an + p
这里需要判断一下是不是超出了中间不变的部分 (区间是[bb + 1, aa])如果超出则s2 = 0;
下一阶段的a1还需要计算大小是多少,就是 a1 = (1 + bb) - (a1 - aa -1)
注意:有一些情况是第一个等差数列是不存在的,则s1 = 0,然后从s2计算,如果s2 也为0,以此类推 (如下图)
题目中要求[a, b], [c, d] 两个区间内的比例,可以转换成 [0, b], [0, d] 减去 [0, b], [0, c - 1] 减去 [0, a - 1], [0, d] 加上 [0, a - 1],
[0, c - 1] ;
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define ll long long
const int maxn = 1e5 + 5;
ll a, b, c, d, p, m;
ll f(ll cc, ll dd) {// 代表[0, cc] [0, dd]
if(cc < 0 || dd < 0) return 0; //如果右端点有一个小于0 就直接返回0
ll s1 = 0, s2 = 0, s3 = 0, a1, an, n;// s1是第一个等差数列的和,s2是相等部分的和,s3是最后一个等差数列的和 ,a1是首项,an是末项,n是项数
ll aa = max(cc, dd);
ll bb = min(cc, dd); //由于结果与两个区间哪个大哪个小没有关系,所以可以先把区间大小确定下来
n = (bb - m) / p + 1; //项数
s1 = n * (m + 1) + n * (n - 1) * p / 2; //前n项和
an = m + 1 + (n - 1) * p; //末项
if(m > bb) // 判断有没有第一个等差数列
s1 = 0, an = m + 1 - p; //相当于第一个等差数列的末项就是中间部分的首项减去p
ll flag = (aa - an - p) ; //用来判断中间部分有没有符合条件的
if(flag < 0) {//如果中间部分没有符合条件的
s2 = 0;
if(an + p <= aa + bb + 1)//判断第三部分的首项超没超出区间上限
a1 = (1 + bb) - (an + p - aa - 1);
else
a1 = 0;
}
else {
s2 = s2 / p + 1;
if(an + p + s2 * p <= aa + bb + 1)//判断第三部分的首项超没超出区间上限
a1 = (1 + bb) - (an + p + s2 * p - aa - 1);
else
a1 = 0;
s2 = s2 * (bb + 1);
}
n = a1 / p + 1;
s3 = n * a1 - n * (n - 1) * p / 2;
return s1 + s2 + s3;
}
int main()
{
int T;
scanf("%d", &T);
for(int k = 1; k <= T; k++) {
scanf("%lld%lld%lld%lld%lld%lld", &a, &b, &c, &d, &p, &m);
printf("Case #%d: ",k);
ll sum = (b - a + 1) * (d - c + 1);//选择的总可能数
ll son = f(b, d) - f(a - 1, d) - f(b, c - 1) + f(a - 1, c - 1); //符合条件的数
if(son == 0)
printf("0/1\n");
else {
ll gcd = __gcd(sum, son);
printf("%lld/%lld\n",son / gcd, sum / gcd);
}
}
return 0;
}