BZOJ1406[AHOI2007密码箱]

http://www.lydsy.com/JudgeOnline/problem.php?id=1406

Description
在一次偶然的情况下,小可可得到了一个密码箱,听说里面藏着一份古代流传下来的藏宝图,只要能破解密码就能打开箱子,而箱子背面刻着的古代图标,就是对密码的提示。经过艰苦的破译,小可可发现,这些图标表示一个数以及这个数与密码的关系。假设这个数是n,密码为x,那么可以得到如下表述: 密码x大于等于0,且小于n,而x的平方除以n,得到的余数为1。 小可可知道满足上述条件的x可能不止一个,所以一定要把所有满足条件的x计算出来,密码肯定就在其中。计算的过程是很艰苦的,你能否编写一个程序来帮助小可可呢?(题中x,n均为正整数)
Input
输入文件只有一行,且只有一个数字n(1<=n<=2,000,000,000)。
Output
你的程序需要找到所有满足前面所描述条件的x,如果不存在这样的x,你的程序只需输出一行“None”(引号不输出),否则请按照从小到大的顺序输出这些x,每行一个数。
Sample Input
12
Sample Output
1
5
7
11

显然O(N)是不行的。我们有:

x^2≡1(mod n)

x^2=kn+1

x^2-1=kn

(x-1)(x+1)=kn

因为(x-1)(x+1)|n,则(x-1)(x+1)的约数一定是n的约数。我们可以先分解出n的约数t[i],再枚举j,使t[i]*j=x-1或x+1,再计算出(x+1)=t[i]*j+2或(x-1)=t[i]*j-2,验证是否符合(x-1)(x+1)|n,若符合,判重后加入答案表。最后排序后输出即可。

有几个容易写挫的地方:1、几个变量要用int64 2、数组不要开小 3、计算(x-1)(x+1)时先分别对n取mod后乘,否则会溢出,此对判别乘积的mod无任何影响。

program p1406;

Var
 i,j,ct,top:longint;
 t,ans:array[0..60000] of int64;
 y,n:int64;

procedure fopen;
  begin
  assign(input,'p1406.in');
  assign(output,'p1406.out');
  reset(input);
  rewrite(output);
end;

Procedure fclose;
  begin
  close(input);
  close(output);
end;

Function inset(P:int64):boolean;
var
 i:longint;
  begin
  for i:=1 to top do
    if ans[i]=p then exit(true);
  exit(false);
end;

Procedure insert(P:int64);
  begin
  inc(top);
  ans[top]:=p;
end;

  begin
  fopen;
  readln(n);ct:=0;
  fillchar(ans,sizeof(ans),0);
  fillchar(t,sizeof(t),0);

  for i:=1 to trunc(sqrt(n)) do
    if (n) mod i=0 then
      begin
      inc(ct);
      t[ct]:=i;
      if i*i<>n then
        begin
        inc(ct);
        t[ct]:=n div int64(i);
      end;
    end;
  
  top:=0;
  if n>1 then insert(1);

  for i:=1 to ct do
  if t[i]<n-1 then
    for j:=1 to round(sqrt(n))+10 div t[i] do
    if ((t[i]*j mod n)*((t[i]*j+2) mod n)) mod n=0 then
      if t[i]*j+1<n then
    
      if not inset(t[i]*j+1) then insert(t[i]*j+1);
   for i:=1 to ct do
    if t[i]>2 then
    for j:=1 to round(sqrt(n))+10 div t[i] do
    if ((t[i]*j mod n)*((t[i]*j-2) mod n)) mod n=0 then
    if (t[i]*j-1<n) and (t[i]*j-1>0) then
      if not inset(t[i]*j-1) then insert(t[i]*j-1);
 
  for i:=1 to top do
    for j:=i+1 to top do
      if ans[i]>ans[j] then
        begin
        y:=ans[i];
        ans[i]:=ans[j];
        ans[j]:=y;
      end;
  if top=0 then writeln('None');
    for i:=1 to top do
    writeln(ans[i]);
  fclose;
end.
原文地址:https://www.cnblogs.com/htfy/p/2813448.html