题目大意:有n个区间[L,R],找出每个区间满足下列条件的最大的数:
1. 将该数平分成两部分,如果该数的位数是奇数则让右侧部分比左侧部分多一位,如123456分成123和456两部分,12345分成12和345两部分,我们把这两部分分别叫pre和past。
2. 使得这两部分的最大公约数大于1.
解题思路:如果直接暴力解决从R开始一个一个判断到L,知道找到符合题意的,可能会超时。这里需要用到素数的部分内容, 区间最大为11-1e13,所以平分成两部分最大不会超过1e7,找出小于1e7的所有素数。
我们从n=R开始找一直找到n=L,在判断时会出现以下几种情况:
- 当pre为素数时,并且pre <= past,这时符合题意的最大的数一定是当past为pre的倍数时,因为pre是素数所以pre的因式只有1和pre,又因为pre和past的公因式不能为1,所以符合题意的past只能是pre的倍数。所以此时结果为n - past % pre,就是当past等于距离past最近的pre的倍数时的结果。
- 当pre为素数时,并且pre >past,这是因为pre的因式只有1和pre,所以past从0到past都一定不满足题意。这时可以从n跳到n-past-1,这样就减少了大量判断。
- 当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;
}