Just Random HDU - 4790 (思维+模拟)

17 篇文章 0 订阅
8 篇文章 0 订阅

 题目链接: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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值