洛谷P4296 [AHOI2007] 密码箱——题解

题目传送:https://www.luogu.com.cn/problem/P4296

跟扩展欧几里得和中国剩余定理并没有什么关系。。

没有几个题解的明确、简便地说明复杂度,本文会简单说明一下。注:本文计算复杂度时的误差极大,只求规模与实际复杂度差不多。

 

 

 

 

 

 

 

   即n|(x-1)(x+1),可看做(x-1)提供了一部分n的因数,(x+1)提供了另一部分n的因数,即 存在整数a,b,a|x-1,b|x+1,a*b=n。

  那么对于一个x若同时满足a|x-1,b|x+1,a*b=n  ,则x就是一个可行的答案。由于满足a*b=n的无序数对最多有sqrt(n)个,可以考虑枚举所有a,b,去找所有能满足a|x-1,b|x+1的x以求得答案。

  不妨设a<b,又有a*b=n,则有1<=a<=sqrt(n)<=b<=n,则可以从1到sqrt(n)枚举可能的a,而b=n/a。但上文“a|x-1,b|x+1”中a不一定<=b,故对于当前枚举的a和b,要找能满足(a|x-1&&b|x+1)||(a|x+1&&b|x-1)的x。

  x满足a|x-1&&b|x+1,即x-1=ka,x+1=k'b。按顺序的思路,可令x=ka+1(即枚举k),判断(x+1)%b。但这样的话,k的规模为n/a,而a<sqrt(n),总复杂度上限约为 n/1(如果a从1开始枚举的话)+n/2+。。。+n/sqrt(n)约为n ln sqrt(n)可能比O(n)暴力都慢了就离谱了(实际上这个题暴力都能拿100分)。

    一般人可能到这就放弃这个思路了,但大佬发现枚举顺序不是唯一的,还可以令x=k‘b-1(即枚举k'),判断(x-1)%a。因为b>=sqrt(n),故k’的规模为n/b<=sqrt(n),则此时最坏的总复杂度上限级别也达不到1(a从1开始枚举的话,b一开始就等于n了)+2+。。。+sqrt(n)约等于n(等差数列求和)。实际上因数个数与数据规模的关系有下图: 

 

    按照这种趋势的话,就算是n<=2e9,有最多因数的数的因数个数估计也不会超过3000吧,那也比sqrt(n=2e9)(约为44721.359549995793928183473374626)小多了,即上文的数列的项数只有3000项不到,用等差数列求和估计出的复杂度上限在n的值在2e9附近时也只有1500sqrt(n)约为6e7。实际上这个级别的复杂度上限是很难达到的,就算再加个log来维护答案的有序、唯一性问题也不大。(更何况本题最极限的数据n=2e9的因数只有区区110个,一点也不极限)

 本人用的数组存储答案,最后sort一遍,输出时防重处理。

ac代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 
 7 #define ll long long
 8 #define ull unsigned long long
 9 #define min(a,b) ((a)>(b)?(b):(a))
10 #define max(a,b) ((a)>(b)>(a):(b))
11 #define swap(a,b) ((a)^=(b),(b)^=(a),(a)^=(b))
12 
13 using namespace std;
14 
15 const int N=2e9;
16 
17 int n,ans[2000000],cnt;//赌一手答案不会超过2000000个。不想赌的话可用别的数据结构或vector,这里懒得改了。 
18 
19 inline int read()//int N
20 {
21     int x=0;
22     bool f=0; 
23     char ch=getchar();
24     while(!isdigit(ch)) ch=getchar(),f|=ch=='-';
25     while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
26     return f?-x:x;
27 } 
28  
29 int main()
30 {
31     n=read();
32     if(n==1)//唯一无解的情况 
33     {
34         cout<<"None";
35         return 0;
36     }
37     ans[++cnt]=1;//x以后大于1,即无x-1=0的情况了 
38 //    (x=1的情况可能会判断多次,导致去重前的答案序列会有很多1,不如在枚举前就处理一下)
39     long long b,x;
40     for(long long a=1;a*a<=n;++a)
41         if(n%a==0)
42         {
43             b=n/a;
44             for(x=b+1;x<n;x+=b)
45             {
46                 if((x+1)%a==0)
47                     ans[++cnt]=x;
48             }
49             x=b-1;
50             if(x==1)
51                 x+=b;
52             for(;x<n;x+=b)//有位大佬说的好,4e9在int范围外。所以涉及计算的量都要开long long 
53             {
54                 if((x-1)%a==0)
55                     ans[++cnt]=x;
56             }
57         }        
58     sort(ans+1,ans+cnt+1);
59     int las=0;
60     for(int i=1;i<=cnt;++i)
61         if(ans[i]!=las)
62         {
63             las=ans[i];
64             printf("%d\n",ans[i]);
65         }
66     return 0;
67 }

 

 

图片引用:

https://www.luogu.com.cn/blog/SuperTNT/solution-p4296

https://www.luogu.com.cn/blog/lemir3/solution-p4296

https://www.cnblogs.com/InductiveSorting-QYF/p/15168013.html

 

posted @ 2021-08-30 14:40  千叶繁华  阅读(73)  评论(0编辑  收藏  举报