洛谷 P8814 [CSP-J 2022] 解密(T2)

目录

题目传送门

部分分

60 分算法

算法

代码

提交结果

70 分算法

算法

代码

提交结果

AC 算法

算法

代码

提交结果

尾声


题目传送门

[CSP-J 2022] 解密 - 洛谷icon-default.png?t=N7T8https://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

里面有许多地方需要判断:

  1. (p + q) ^ 2 - 4pq(即 (p - q) ^ 2) 如果是负数,输出 NO
  2. 如果 (p + q) ^ 2 - 4pq(即 (p - q) ^ 2)不等于 (p - q) * (p - q)(即开不开根号),输出 NO
  3. 最后 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~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值