zoukankan      html  css  js  c++  java
  • 整除

    定义

      a是b的因数,或b是a的倍数,记作a|b

      整除的性质:

        ①如果a|b,b|c,那么a|c;

        ②a|b且a|c等价于对于任意整数x、y满足a|(b*x+c*y);

        ③设m≠0,那么a|b等价于(m*a)|(m*b);

        ④设整数x、y满足a*x+b*y=1,且a|n,b|n,那么(a*b)|n;

          证明:∵a|n且b|n

             ∴(a*b)|(b*n)且(a*b)|(a*n)

             ∴(a*b)|(a*n*x+b*n*y)

             又∵a*n*x+b*n*y=(a*x+b*y)*n=n

             ∴(a*b)|n。

        ⑤若b=q*d+c,那么d|b的充要条件是d|c。

    例1:church

      给出m*n个矩形,每个整数节点上有一个教堂,每个教堂与它八个方向的教堂有连边,求从某一点开始经过所有点的最短路径。

      范围:m、n≤10000

      首先我们没有特别好的思路,就考虑爆搜打表,打出表后比较和n、m的关系,我们发现对于一般的n、m,满足如果n*m为偶数,那么最少长度即为m*n,否则为m*n+0.414,由于m==1||n==1时不存在1.414的边,特判即可。

    例2:strongbox

      一个密码满足都是[0,n-1]的整数,并且如果a是密码,b是密码,那么(a+b)%n都是密码,给出n、k和k个整数,其中1~k-1非密码,k为密码,求最多密码数。

      范围:1≤k≤250000,k≤n≤1014

      结论1:如果x是密码,那么x*k%n也是密码。

      证明:显然,它并没有要求a、b不相同,那么x的整数次倍一定是密码。

      结论2:如果x是密码,那么gcd(x,n)也是密码。

      证明:我们考虑x*k%n=gcd(x,n),我们可以转化为x*k-n*c=gcd(x,n)

           ∵对于二元一次方程ax+by=c,有整数解当且仅当c%gcd(a,b)==0

           ∴上述方程对于k一定有正整数解

           ∴gcd(x,n)也是密码。

      结论3:如果x、y是密码,那么gcd(x,y)也是密码

      证明:由结论1知(a*x+b*y)%n也是密码

           ∵a*x+b*y=gcd(x,y)一定有解

           ∴a*x+b*y≡gcd(x,y)(mod n)一定有解(相等即可)

           ∵a*x+b*y%n一定是密码

           ∴gcd(x,y)一定是密码

      

      接下来我们考虑如何利用这几个结论,考虑对于一个密码集合S,设A中所有数的gcd为x

      那么我们可以证明不存在比x小的数存在于S中,否则x将不是所有数的gcd

      所以S中的数即为x、2x、3x……

      因此我们要密码集合尽可能多,那么我们就是要x尽可能的小。

      所以对于一个密码,我们可以先用gcd(a[k],n)将a[k]缩小,可知x是a[k]的因数,但它不是其他任何非密码的因数,因此我们也可以gcd(a[i],a[k])把a[i]缩小,因为不是a[k]可以直接排除,这样之后的a[i]也一定是a[k]的因数。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
    ll a[310000],fac[310000];
    bool f[310000];
    int main()
    {
        ll n,k,cnt=0;
        scanf("%lld%lld",&n,&k);
        for(ll i=1;i<=k;i++)
            scanf("%lld",&a[i]);
        a[k]=gcd(a[k],n);
        for(ll i=1;i<k;i++)
            a[i]=gcd(a[i],a[k]);
        for(ll i=1;i*i<=a[k];i++)
            if(a[k]%i==0)
            {
                fac[++cnt]=i;
                if(i*i!=a[k])fac[++cnt]=a[k]/i;
            }
        sort(fac+1,fac+cnt+1);
        for(ll i=1;i<k;i++)
        {
            ll pos=lower_bound(fac+1,fac+cnt+1,a[i])-fac;
            if(pos<=cnt)f[pos]=1;
        }
        for(ll i=1;i<=cnt;i++)
            if(f[i])
            for(ll j=1;j<i;j++)
                if(fac[i]%fac[j]==0)f[j]=1;
        for(ll i=1;;i++)
            if(!f[i]){printf("%lld",n/fac[i]);break ;}
    }

      

  • 相关阅读:
    C#基础概念二十五问
    Jpage分页 通用.net2.0分页程序
    使用ADO.net转换数据到Excel格式并提供下载
    .Net中FckEditor的配置和使用方法(含示例源码)
    利用Wildcard ISAPI Mapping隐藏扩展名[转]
    写简历最易犯五大愚蠢错误
    程序员35岁前成功的12条黄金法则
    目前较为流行的Ajax框架一览
    身份证的验证(支持15位与18位)
    使用ISAPI_Rewrite对asp.net实现URL重写伪静态
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11672595.html
Copyright © 2011-2022 走看看