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) x2≡1(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)(x−1)=kn
考 虑 拆 解 k , n k,n k,n
① ( x + 1 ) = k 1 ∗ n 1 (x+1)=k_1*n_1 (x+1)=k1∗n1
② ( x − 1 ) = k 2 ∗ n 2 (x-1)=k_2*n_2 (x−1)=k2∗n2
③ k 1 ∗ k 2 = k , n 1 ∗ n 2 = n k_1*k_2=k,n_1*n_2=n k1∗k2=k,n1∗n2=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)=k1∗y④
因为 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)=d∗y
即 x = d ∗ y − 1 x=d*y-1 x=d∗y−1⑤
④⑤带入②,则有 ( d ∗ y − 2 ) = k 2 ∗ ( n / y ) (d*y-2)=k_2*(n/y) (d∗y−2)=k2∗(n/y)
若 ( d ∗ y − 2 ) (d*y-2) (d∗y−2) 为 ( n / y ) (n/y) (n/y) 的倍数,则 x = d ∗ y − 1 x=d*y-1 x=d∗y−1 为一组符合的解
同理可以将 y y y 记作其中的 n 2 n_2 n2,即由②推①
则有 ( d ∗ y + 2 ) = k 1 ∗ ( n / y ) (d*y+2)=k1*(n/y) (d∗y+2)=k1∗(n/y)
若 ( d ∗ y + 2 ) (d*y+2) (d∗y+2) 为 ( n / y ) (n/y) (n/y) 的倍数,则 x = d ∗ y + 1 x=d*y+1 x=d∗y+1 为一组可能的解,
因为这里是 ( x − 1 ) − > ( x + 1 ) (x-1)->(x+1) (x−1)−>(x+1), x x x 可能大于 n n n
ps:
- 一组约数
y
y
y ,
n
/
y
n/y
n/y,可能对应两个解
即 y − > x − 1 , ( n / y ) − > x + 1 y->x-1,(n/y)->x+1 y−>x−1,(n/y)−>x+1 或者 y − > x + 1 , ( n / y ) − > x − 1 y->x+1,(n/y)->x-1 y−>x+1,(n/y)−>x−1 - 枚举倍数时,选择大于
n
\sqrt{n}
n 的,
不然 ∑ ( k i ) \sum(k_i) ∑(ki) 可能和会很大
然后对于每个枚举的 约数,
可以 ( x − 1 ) − > ( x + 1 ) (x-1)->(x+1) (x−1)−>(x+1) 或者 ( x + 1 ) − > ( x − 1 ) (x+1)->(x-1) (x+1)−>(x−1)
#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]);
}