第十三届蓝桥杯省赛A组题解传送门
题目大意
给定T个正整数n,分别问每个n能否表示为xy11×xy22的形式
其中x1,x2为正整数,y1,y2 为大于等于2的正整数
解题思路
完全没有数论基础的话可以先看看质数专题和约数专题
由算数基本定理知任何一个大于1的正整数都能唯一分解成有限个质数的乘积,
即N=p1c1p2c2⋯pmcm
首先先明确,如果存在一个ci=1,那么一定是no
这是显然的,否则y1,y2 不可能为大于等于2的正整数
那么就是对于每一个质因数的指数ci(ci≥2)
我们要解一个这样的方程:ci=k1×y1+k2×y2,其中k1,k2为非负整数,y1,y2≥2
观察样例,可以推测y1=2,y2=3时一定有非负整数解
然后我们试着证明:
2×k1+3×k2=k,k≥2
当k\equiv 0\pmod 3时,k_1=0,k_2=\frac{k}{3}
当k\equiv 1\pmod 3时,k_1=2,k_2=\frac{k-4}{3}
当k\equiv 2\pmod 3时,k_1=1,k_2=\frac{k-2}{3}
证毕,发现推测是对的
所以问题转变成,x能否表示成x_1^{2}\times x_2^{3}的形式
当x_1是1时,检查是否是立方数即可
当x_2是1时,检查是否是平方数即可
然后现在解决当x_1和x_2都不是1时的情况
若能拆解,则x=x_1^{2}\times x_2^{3}
则x\geq min(x_1,x_2)^5,即min(x_1,x_2)\leq x^{\frac{1}{5}}
x最大是10^{18},开五次根号后不超过4000
就是说,对于超过4000的x的质因数p_i,它的指数c_i一定不超过5(如果超过了那直接比x都大了)
那么我们先线性筛处理出来不超过4000的质数
然后把其中是x的因数的除掉
过程中间如果发现一个c_i=1,直接可以知道是no(不要忘记刚开始的这个结论)
如果整个过程顺利地完成了,此时x可能还剩下由大于4000的质因数构成的数
那么我们利用这些质数的指数一定不超过5且不能是1的这两个结论,知道指数一定是2,3,4
也就是说,剩下的x,一定还是要么平方数要么立方数
那么就再check一次即可
具体代码
#include <bits/stdc++.h>
typedef long long LL;
const int N = 4010;
int v[N]; // v[i]记录数字i的最小质因子
int prime[N], m;
void init() //线性筛
{
memset(v, 0, sizeof v); // v[i]记录数字i的最小质因子
m = 0; // m记录质数个数
for (int i = 2; i <= 4000; ++i)
{
if (v[i] == 0) // i是质数
{
v[i] = i;
prime[++m] = i;
}
for (int j = 1; j <= m; ++j)
{
//若i有比prime[j]更小的质因子,或超出n的范围,则停止循环
if (prime[j] > v[i] || prime[j] > 4000 / i)
break;
// prime[j]是合数i*prime[j]的最小质因子
v[i * prime[j]] = prime[j];
}
}
}
bool check(LL x) //判断一个数是否是平方数或立方数
{
LL l = 1, r = 2e9;
while (l < r)
{
LL mid = l + r >> 1;
if (mid * mid >= x)
r = mid;
else
l = mid + 1;
}
if (l * l == x)
return true;
l = 1, r = 2e6;
while (l < r)
{
LL mid = l + r >> 1;
if (mid * mid * mid >= x)
r = mid;
else
l = mid + 1;
}
if (l * l * l == x)
return true;
return false;
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int T = 1;
std::cin >> T;
init();
while (T--)
{
LL x;
std::cin >> x;
if (check(x))
{
std::cout << "yes" << '\n';
continue;
}
bool impossible = false;
for (int i = 1; i <= m; ++i)
{
if (x % prime[i] == 0)
{
int t = 0;
while (x % prime[i] == 0)
{
++t;
x /= prime[i];
}
if (t == 1)
{
impossible = true;
break;
}
}
}
if (!impossible && check(x))
std::cout << "yes" << '\n';
else
std::cout << "no" << '\n';
}
}
牛逼 这个数据范围上来给我干蒙了
您好,我有一个地方不是很理解,就是一个数如果是2^43^57^3 这个怎么可以拆成两个数的乘积呢?这个在您的代码中也会是yes,求教!
2^43^57^3=4^23^57^3=(43)^2(3*7)^3
#include<bits/stdc++.h> using namespace std; const int N = 4000; bool st[N]; int primes[N]; int cnt; void get_prime(int x){ for(int i = 2; i <= x; i ++){ if(!st[i]) primes[cnt ++] = i; for(int j = 0; primes[j] <= x / i;j ++){ st[i * primes[j]] = true; if(i % primes[j] == 0) break; } } } bool check(int x){ int l = 0, r = x; while(l < r){ int mid = (l + r) >> 1; if(mid * mid >= x) r = mid; else l = mid + 1; } if(l * l == x) return false; l = 0, r = x; while(l < r){ int mid = (l + r) >> 1; if(mid * mid * mid >= x) r = mid; else l = mid + 1; } if(l * l * l == x) return false; return true; } int main(){ get_prime(N - 1); int t; scanf("%d", &t); while(t -- ){ int a; scanf("%d", &a); if(!check(a)){ puts("yes"); continue; } bool fg = true; for(int i = 0; i < cnt; i ++){ if(a % primes[i] == 0){ int t = 0; while(a % primes[i] == 0){ a /= primes[i]; t ++; } if(t == 1){ puts("no"); fg= false; break; } } } if(!fg) continue; if(!check(a)){ puts("yes"); continue; } puts("no"); } }
看了您的思路写的,貌似tle了…呜呜呜
好厉害!!!真的思路清晰!!我分析的时候光分析出来了应该是二次方和三次方两种情况,但是没想到还有4000这个限制条件 tql!
Orz