目录
题目传送门
[CSP-J 2022] 解密 - 洛谷https://www.luogu.com.cn/problem/P8814
部分分
60 分算法
算法
先说一下部分分吧
可以对 n 进行因数分解,然后每次看合不合法就可以了
因数分解怎么分解,可以枚举 1 到根号 n,即
for(p = 1; p * p <= n; ++p)
然后看如果 n 能除开 p,就让 q 等于 n 除以 p,就可以看 p 和 q 合不合法了
这样的话时间复杂度 O(k 根号 n),应该能过到第六个点,可以看到,前六个点的数据范围都不超过 int,所以不用开 long long
这个代码应该不用说了吧,那我们直接上代码
代码
#include <iostream>
using namespace std;
int k, n, d, e;
void inp() {
scanf("%d%d%d", &n, &d, &e);
}
void work() {
int p, q;
for(p = 1; p * p <= n; ++p) {
if(n % p == 0) {
q = n / p;
if(e * d == (p - 1) * (q - 1) + 1) {
printf("%d %d\n", p, q);
return;
}
}
}
puts("NO");
}
int main() {
scanf("%d", &k);
for(int i = 0; i < k; ++i) {
inp();
work();
}
return 0;
}
提交结果
不出所料,果然 60 分
70 分算法
算法
观察到第 7 个点说:保证若有解则 p=q
所以可以看只要 k 大于 10^3,那就把 p 和 q 都设成 n 的开方,然后测一下 p 和 q,看合不合法就行了
但这样 n 就会爆 int,因此我们要定义成 long long
代码
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
ll k, n, d, e;
void inp() {
scanf("%lld%lld%lld", &n, &d, &e);
}
bool check(ll p, ll q) {
if(e * d != (p - 1) * (q - 1) + 1)
return false;
return true;
}
void brute_work() {
ll p, q;
for(p = 1; p * p <= n; ++p) {
if(n % p == 0) {
q = n / p;
if(check(p, q)) {
printf("%lld %lld\n", p, q);
return;
}
}
}
puts("NO");
}
void subtask_work()
{
ll p = sqrt(n);
if(check(p, p))
printf("%lld %lld\n", p, p);
else
puts("NO");
}
int main()
{
scanf("%d", &k);
for(int i = 0; i < k; ++i) {
inp();
if(k > 1000)
subtask_work();
else
brute_work();
}
return 0;
}
提交结果
这样的话,就多过了一个点
70分选手!
AC 算法
算法
既然暴力不行,我们就试一下数学
过程:
e * d = (p - 1)(q - 1) + 1(已知)
e * d = p * q - p - q + 2(将括号展开)
注意到 p * q = n
e * d = n - (p + q) + 2(将 p * q 替换成 n)
p + q = n - e * d + 2(移项)
现在就需要通过 (p * q) 和 (p + q) 来推出 p, q
注意到 (p + q) ^ 2 = p ^ 2 + q ^ 2 + 2pq(完全平方公式:(a + b)^2 = a^2 + 2ab + b^2)
(p + q) ^ 2 - 4pq = p ^ 2 + q ^ 2 - 2pq(两边同时减 2pq)
= (p - q) ^ 2(完全平方公式:(a - b)^2 = a^2 - 2ab + b^2)
p - q = sqrt((p + q) ^ 2 - 4n)(两边同时开根号)
现在知道了 p + q、p - q
p = ((p + q) + (p - q)) / 2( ((p + q) + (p - q)) / 2 = (p + q + p - q) / 2 = 2p / 2 = p)
q = n / p
里面有许多地方需要判断:
- (p + q) ^ 2 - 4pq(即 (p - q) ^ 2) 如果是负数,输出 NO
- 如果 (p + q) ^ 2 - 4pq(即 (p - q) ^ 2)不等于 (p - q) * (p - q)(即开不开根号),输出 NO
- 最后 p * q 不等于 n(即 n 除不开 p),就输出 NO
最后还有一点需要注意:题目让从小到大输出 p 和 q,所以最后如果 p 大于 q,就 swap 一下
代码
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
ll k, n, d, e;
void inp() {
scanf("%lld%lld%lld", &n, &d, &e);
}
void work() {
ll p_add_q = n - e * d + 2;
ll p_sub_q_2 = p_add_q * p_add_q - 4 * n;
if(p_sub_q_2 < 0) {
puts("NO");
return;
}
ll p_sub_q = sqrt(p_add_q * p_add_q - 4 * n);
if(p_sub_q_2 != p_sub_q * p_sub_q) {
puts("NO");
return;
}
ll p = (p_add_q + p_sub_q) / 2;
ll q = n / p;
if(p * q != n) {
puts("NO");
return;
}
if(p > q)
swap(p, q);
printf("%d %d\n", int(p), int(q));
}
int main() {
scanf("%d", &k);
for(int i = 0; i < k; ++i) {
inp();
work();
}
return 0;
}
提交结果
提交一下哈~
AC ! ! !
尾声
如果这篇博客对您(您的团队)有帮助的话,就帮忙点个赞,加个关注!
最后,祝您(您的团队)在 OI 的路上一路顺风!!!
┬┴┬┴┤・ω・)ノ Bye~Bye~