[Kuangbin带你飞]专题十四 数论(二)

青蛙的约会  扩展欧几里得法求解同余方程 

题意:两只青蛙,沿着一条首位相连的数轴在跳,数轴长度为L,A在x处,一次可以跳m米,B在y处,一次可以跳n米,问跳了几次后碰面。

x+mt\equiv y+nt (mod L)    移项得(m-n)t\equiv y-x(mod L) 即(m-n)*t+k*L=y-x 

注意y-x必须大于0

关于扩展欧几里得的证明 证明2

我代码中a(m-n)+kL=y-x

运用extgcd(m-n,L,a,k) 得到的a是 \frac{m-n}{gcd(m-n,L)}*a+\frac{L}{gcd(m-n,L)}*k=1的解,要得到原方程的解,还需要

\frac{a*(y-x)}{gcd(m-n,L)} \% \frac{L}{gcd(m-n,L)} 才是(m-n)*a'+L*k'=y-c的解

以及(y-x)无法被gcd(m-n,L)整除的话,无解

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL x,y,m,n,L;
LL extgcd(LL a,LL b,LL &x,LL &y)  //返回gcd(a,b)  ax+by=1
{
    if(a==0&&b==0) return -1;
    if(b==0) {x=1;y=0;return a;}
    LL d=extgcd(b,a%b,y,x);
    y=y-a/b*x;
    return d;
}
LL mod_inverse(LL a,LL n)
{
    LL x,y;
    LL d=extgcd(a,n,x,y);            //由扩展欧几里得得到一个特解x
    if(d==1) return (x%n+n)%n;      //+n 防负数。。   x变为最小非负整数解
    else return -1;
}

int main()
{
//    if(x<0) x=L+x;
//    if(y<0) y=L+y;   原先我这么写可真是傻逼啊,我应该让y-x>0,同步改
//设时间为t,则两个青蛙的位置分别为(x+mt)mod L、(y+nt) mod L,相遇即是(x+mt)%L=(y+nt)%L,即(m-n)*t+k*L=y-x
   LL x,y,m,n,L;
   while(cin>>x>>y>>m>>n>>L)
   {
       if(m<n) swap(m,n),swap(x,y);  //这行代码是必要的  extgcd函数是必须的
       LL a,k;
       LL c=y-x;
       LL d=extgcd(m-n,L,a,k);
       LL r=mod_inverse((m-n)/d,L/d);
       if(c%d) cout<<"Impossible"<<endl;    //c不是d的倍数,则无解
       else cout<<((r*c/d)%(L/d)+L/d)%(L/d)<<endl;  // (a*c/d)%(L/d)+(L/d)也对   这个是最小正答案
   }
   return 0;
}

C-LOOPs

题意:for (variable = A; variable != B; variable += C) 这个循环在mod 2^k下执行了几次

cx\equiv b-a(mod 2^k)  -> cx+2^ky=b-a -> extgcd(c,2^k,x,y)

注意点 2^k会超int。  LL mod=1<<k LL mod=LL(1<<k).  LL mod=1LL<<k 或者 LL mod=(LL)1<<k ,要先把1转换成LL型

话说我经常明明想到要LL,但是却在中间过程死掉。最遗憾的一次是扇贝杯,最难受的一次是队友写的一道线段树,传参数的时候中间值爆int了,对比了正确代码怎么都找不出错误,最后问了sdn大佬一眼指出问题_(:з」∠)_ 

#include<stdio.h>
#include<iostream>
using namespace std;
typedef long long LL;
LL extgcd(LL a,LL b,LL &x,LL &y)  //返回gcd(a,b)  ax+by=1
{
    if(a==0&&b==0) return -1;
    if(b==0) {x=1;y=0;return a;}
    LL d=extgcd(b,a%b,y,x);
    y=y-a/b*x;
    return d;
}
LL mod_inverse(LL a,LL n)
{
    LL x,y;
    LL d=extgcd(a,n,x,y);
    if(d==1) return (x%n+n)%n;
    else return -1;
}
int main()
{
   LL A,B,C,k;
   while(cin>>A>>B>>C>>k&&!(!A&&!B&&!C&&!k))  //不能四个同时为0
   {
       //if(C==0&&A!=B) {cout<<"FOREVER"<<endl; continue;}
       LL x,y;
      // LL mod=1LL<<k;   这样OK
       LL mod=(LL)1<<k;   //(LL)(1<<k)也是错的
       LL d=extgcd(C,mod,x,y);
       if((B-A)%d) {cout<<"FOREVER"<<endl; continue;}
       else
       {
           LL r=mod_inverse(C/d,mod/d);
           cout<<(r*(B-A)/d%(mod/d)+mod/d)%(mod/d)<<endl;  //r和x都可以

       }
   }
   return 0;
}

 

Prime Time  

题意:n^2+n+41在[a,b]区间,素数的占比。注意rounded to two decimal digits是要四舍五入到小数点后第二位

UVA 10200 我的题解  hin奇怪我这题的vjudge提交记录都没了

fairy sequence

题意:Fn是所有分母<=n,分子分母互素的分数的集合。问Fn的大小是多少

思路:有O - GCD - Extreme (II)的铺垫,这道题就很简单

Fn是Fn-1 加上以n为分母的数,所以F(n)=F(n-1)+\phi(n)

#include<stdio.h>
#include<iostream>
using namespace std;
const int N=1e6+10;
typedef long long LL;
int euler[N];
LL num[N];
void gete()
{
    for(int i=2;i<N;++i)
        euler[i]=i;
    for(int i=2;i<N;++i)
    {
        if(euler[i]==i)   //说明i是素数
        {
            for(int j=i;j<N;j+=i)
                euler[j]=euler[j]/i*(i-1);
        }
    }
//    for(int i=2;i<100;++i)
//        cout<<euler[i]<<endl;
}
void getN()
{
    num[2]=euler[2];
    for(int i=3;i<N;++i)
        num[i]=num[i-1]+euler[i];
}
int main()
{
    gete();
    getN();
    int n;
    while(cin>>n&&n)
    {
        cout<<num[n]<<endl;
    }
    return 0;
}

UVA 11752 题目链接打不开了 但我学到的还是要记录的

题意:super power numbers指的是可以表示成两个整数的次幂形式的数,如64=4^4 64=8^2。要求输出[1,2^64-1]范围内所有的super power numbers

64=2^6=2^(2*3)   可知要想成为超级数,得是一个数的合数次幂,易知这个合数次幂大于等于4(最小合数),小于等于64(最小的底数就是2)

所以对底数遍历,指数的上界是upper=ceil(64*log(2)/log(i));小于该上界的合数次幂都是超级数

#include<iostream>
#include<set>
#include<math.h>
#include<stdio.h>
using namespace std;
typedef long long LL;
const int N=70;
int tag[70];
int prime[70];
int cnt;
int num;
int compo[70];
set<LL> s;
void getp()
{
    tag[0]=tag[1]=1;
    for(int i=2;i<N;++i)
    {
        if(!tag[i])
            prime[++cnt]=i;
        for(int j=1;j<=cnt&&prime[j]*i<N;++j)
        {
            tag[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
    for(int i=4;i<64;++i)                       //记录4-64之间的所有合数
        if(tag[i]) compo[++num]=i;

}
int main()
{
   // LL m=1<<64;  不知道为何这个值为0 当然是因为我爆Int啦,气哭
    getp();
    s.clear();
    s.insert(1);
    int L=1<<16;
    for(int i=2;i<=L;++i)   //最大只能是(2^16)^4所以到这
    {
        //cout<<i<<endl;
        int upper=ceil(64*log(2)/log(i));
        for(int j=1;j<=num&&compo[j]<upper;++j)   //未加j<=num,导致有些0 2^0=1,但是对答案应该没有影响
        {
            LL x=(LL) pow(i,compo[j]);
            s.insert(x);
        }
    }
    //s.erase(s.begin());
    for(set<LL>::iterator it=s.begin();it!=s.end();++it)
        cout<<*it<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值