• 洛谷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
    ",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

  • 相关阅读:
    Linux基本结构
    Linux诞生
    Python之克隆
    Python之数据类型转换
    gb18030与utf-8
    for循环与while循环
    code::blocks调试
    关于隐式创建vue实例实现简化弹出框组件显示步骤
    blob canvas img dataUrl的互相转换和用处
    观察者模式与发布订阅模式的区别
  • 原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/15205228.html
Copyright © 2020-2023  润新知