【数论】【Luogu P4296】Day4-D/密码箱

链接

D
Luogu P4296

题目描述

给定n,求出所有正整数x,满足小于n,且x^2(%n)=1
将满足的x按从小到大的顺序依次换行输出

思路

x 2 ≡ 1 (   m o d     n ) x^2 \equiv 1(\bmod\ n) x21(mod n)
( x + 1 ) ( x − 1 ) = k n (x+1)(x-1) = kn (x+1)(x1)=kn
( x + 1 ) = k 1 n 1 ( k 1 ∣ k , n 1 ∣ n ) (x+1) = k_1n_1 (k_1|k, n_1|n) (x+1)=k1n1(k1k,n1n
( x − 1 ) = k 2 n 2 ( k 2 ∣ k , n 2 ∣ n ) (x-1) = k_2n_2(k_2|k, n_2|n) (x1)=k2n2k2k,n2n
那么很显然因为 k 1 n 1 ⩽ n k_1n_1 \leqslant n k1n1n,所以 k 1 ⩽ n n 1 k_1 \leqslant \frac{n}{n_1} k1n1n
那肯定不能让 n 1 n_1 n1太小,要不然就T飞了
又可以发现 n 1 n 2 = n n_1n_2 = n n1n2=n
那么 n 2 = n n 1 n_2 = \frac{n}{n_1} n2=n1n
那是不是只需要枚举一个大于 n \sqrt{n} n n n n的约数,然后再求 n 2 n_2 n2,验证求出的x是否为 n 2 n_2 n2的倍数就好了

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long

using namespace std;

ll n, cnt, t;
ll ans[500005], an[500005];

bool check(ll a)
{
    
    
	for(int j = 0; j * a <= n; ++j)
	{
    
    
		ll a2 = n / a;
		if((j * a - 2) % a2 == 0) 
			ans[++cnt] = j * a - 1;
		if((j * a + 2) % a2 == 0)
			ans[++cnt] = j * a + 1;
	}	
}

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

猜你喜欢

转载自blog.csdn.net/LTH060226/article/details/119652346