c++ H - Half Nice Years Gym - 101840H SDUT

在这里插入图片描述
在这里插入图片描述

题目大意:有n个区间[L,R],找出每个区间满足下列条件的最大的数:
1. 将该数平分成两部分,如果该数的位数是奇数则让右侧部分比左侧部分多一位,如123456分成123和456两部分,12345分成12和345两部分,我们把这两部分分别叫pre和past。
2. 使得这两部分的最大公约数大于1.

解题思路:如果直接暴力解决从R开始一个一个判断到L,知道找到符合题意的,可能会超时。这里需要用到素数的部分内容, 区间最大为11-1e13,所以平分成两部分最大不会超过1e7,找出小于1e7的所有素数。

我们从n=R开始找一直找到n=L,在判断时会出现以下几种情况:

  1. 当pre为素数时,并且pre <= past,这时符合题意的最大的数一定是当past为pre的倍数时,因为pre是素数所以pre的因式只有1和pre,又因为pre和past的公因式不能为1,所以符合题意的past只能是pre的倍数。所以此时结果为n - past % pre,就是当past等于距离past最近的pre的倍数时的结果。
  2. 当pre为素数时,并且pre >past,这是因为pre的因式只有1和pre,所以past从0到past都一定不满足题意。这时可以从n跳到n-past-1,这样就减少了大量判断。
  3. 当pre不是素数时就可以直接判断pre和past的公因式,这时候注意判断past是否为0 。

这里的pre和past的关系,只有当past等于0了,下一个数pre = pre-1,past则再从最大的一个数减到零,pre再见小一个。所以在情况1.中pre<=past 时结果一定是当past等于pre的倍数时,这是一定是满足题目并且最大。


#include <bits/stdc++.h>

using namespace std;

/*全局变量*/
const int N = 1e7 + 10;
int pre, past;
bool prime[N];

/*自定义函数*/
void isprime();//找出素数
long long gcd(long long a, long long b);//求a,b的最大公约数
void dividual(long long x);//将一个数分成两部分pre和past
int length(long long x);//求x的位数的一半

int main()
{
    int t;
    long long a, b;
    freopen("halfnice.in", "r", stdin);
    isprime();//找出素数有哪些
    cin >> t;
    for (int i = 1; i <= t; i++)
    {
        scanf("%lld%lld", &a, &b);
        bool mark = false;
        for (long long j = b; j >= a; j--)
        {
            dividual(j);//将j分成两部分pre和past
            if (prime[pre] && past >= pre)
           //当pre为素数并且past>=pre,这时候当past减到pre的整数倍时满足条件
            {
                mark = true;
                printf("Case %d: %lld\n", i, j - past % pre);
                break;
            }
            else if (prime[pre] && past < pre)
            //当pre为素数并且past<pre,这时2-past都不会是pre的因式
            {
                j -= past;
            }
            else if (gcd(pre,past) > 1 && past != 0)
            {
                mark = true;
                printf("Case %d: %lld\n", i, j);
                break;
            }
        }
        if (!mark)
        {
            printf("Case %d: impossible\n", i);
        }
    }
}

/*自定义函数*/
void isprime()//找出素数
{
    memset(prime, true, sizeof(prime));
    prime[0] = prime[1] = false;
    for (int i = 2; i * i < N; i++)
    {
        if (prime[i])
        {
            for (int j = i + i; j < N; j += i)
            {
                prime[j] = false;
            }
        }
    }
}
long long gcd(long long a, long long b)//求a,b的最大公约数
{
    if (a < b) long long t = a, a = b, b = t;
    if (b == 0)
    {
        return a;
    }
    else
    {
        gcd(b, a % b);
    }
}

int length(long long x)//求x的位数的一半
{
    int len = 0;
    while (x)
    {
        len++;
        x /= 10;
    }
    if (len % 2 != 0) len++;
    len /= 2;
    return len;
}

void dividual(long long x)//将一个数分成两部分pre和past
{
    int len = length(x);//求x的位数的一半
    int mol = 1;
    while (len--) mol *= 10;
    pre  = x / mol;
    past = x % mol;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

碧羽o(* ̄▽ ̄*)ブ回雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值