luogu P4296 [AHOI2007] 密码箱 [数论] [推柿子]

P4296 [AHOI2007] 密码箱 link

在这里插入图片描述

题目大意:

给定 n n n,求出所有正整数 x x x,满足小于 n n n

x 2 ≡ 1 ( m o d    n ) x^2\equiv1\left(mod\;n\right) x21(modn) 将满足的 x x x 按从小到大的顺序依次换行输出


思路:

x 2 = k n + 1 x^2=kn+1 x2=kn+1

移项发现是个平方差

( x + 1 ) ( x − 1 ) = k n (x+1)(x-1)=kn (x+1)(x1)=kn

考 虑 拆 解 k , n k,n k,n

( x + 1 ) = k 1 ∗ n 1 (x+1)=k_1*n_1 (x+1)=k1n1

( x − 1 ) = k 2 ∗ n 2 (x-1)=k_2*n_2 (x1)=k2n2

k 1 ∗ k 2 = k , n 1 ∗ n 2 = n k_1*k_2=k,n_1*n_2=n k1k2=k,n1n2=n

发现可以枚举 n n n 的约数 y y y

记作其中的 n 1 n_1 n1

则有 n 2 = n / y n_2=n/y n2=n/y, ( x + 1 ) = k 1 ∗ y (x+1)=k1*y (x+1)=k1y

因为 x < n x<n x<n, x + 1 < = n x+1<=n x+1<=n,考虑枚举倍数 k 1 k_1 k1

假设当前枚举倍数为 d d d

则有 ( x + 1 ) = d ∗ y (x+1)=d*y (x+1)=dy

x = d ∗ y − 1 x=d*y-1 x=dy1

④⑤带入②,则有 ( d ∗ y − 2 ) = k 2 ∗ ( n / y ) (d*y-2)=k_2*(n/y) (dy2)=k2(n/y)

( d ∗ y − 2 ) (d*y-2) (dy2) ( n / y ) (n/y) (n/y) 的倍数,则 x = d ∗ y − 1 x=d*y-1 x=dy1 为一组符合的解

同理可以将 y y y 记作其中的 n 2 n_2 n2,即由②推①

则有 ( d ∗ y + 2 ) = k 1 ∗ ( n / y ) (d*y+2)=k1*(n/y) (dy+2)=k1(n/y)

( d ∗ y + 2 ) (d*y+2) (dy+2) ( n / y ) (n/y) (n/y) 的倍数,则 x = d ∗ y + 1 x=d*y+1 x=dy+1 为一组可能的解,

因为这里是 ( x − 1 ) − > ( x + 1 ) (x-1)->(x+1) (x1)>(x+1) x x x 可能大于 n n n

ps:

  1. 一组约数 y y y , n / y n/y n/y,可能对应两个解
    y − > x − 1 , ( n / y ) − > x + 1 y->x-1,(n/y)->x+1 y>x1,(n/y)>x+1 或者 y − > x + 1 , ( n / y ) − > x − 1 y->x+1,(n/y)->x-1 y>x+1,(n/y)>x1
  2. 枚举倍数时,选择大于 n \sqrt{n} n 的,
    不然 ∑ ( k i ) \sum(k_i) (ki) 可能和会很大
    然后对于每个枚举的 约数,
    可以 ( x − 1 ) − > ( x + 1 ) (x-1)->(x+1) (x1)>(x+1) 或者 ( x + 1 ) − > ( x − 1 ) (x+1)->(x-1) (x+1)>(x1)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 1e6 + 10;
ll n, ans[N], num, cnt; 
  
void Jud(ll y) 
{
	for (ll d = 0; d <= n / y; d++) 
	{
		num = (d * y - 2);
		if (!(num % (n / y))) ans[++cnt] = d * y - 1;		
		num = (d * y + 2);
		if (!(num % (n / y))) ans[++cnt] = d * y + 1;
	}
}

int main() {
	scanf("%lld", &n);
	if (n == 1) { printf("None\n"); return 0; }
	ll siz = sqrt(n);
	for(ll i = 1; i <= siz; i++) if (!(n % i)) Jud(n / i);
	sort(ans + 1, ans + cnt + 1);
	int m = unique(ans + 1, ans + cnt + 1) - ans - 1;
	int l = 1, r = m;
	while (ans[r] >= n) r--;
	while (ans[l] < 0) l++; 
	if (!(r - l + 1)) printf("None");
	else for(int i = l; i <= r; i++) printf("%lld\n", ans[i]);
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值