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

Bi-shoe and phi-shoe  知识点:欧拉函数打表

题意:竹子的得分为它长度的欧拉函数值,Bi-shoe想买竹子给同学,每个同学收到的竹子得分>=他的幸运数字,竹子每单位长度需要花1Xukha。问Bi-shoe最少花多少钱?

思路:欧拉值打表,遍历

#include
#include
#include
using namespace std;
const int N=1e4+10;
const int M=1e6+10;
int euler[M];
int a[N];
void gete()
{
    for(int i=1;i<=M;++i)
        euler[i]=i;
    for(int i=2;i<=M;++i)
    {
        if(euler[i]==i)   //说明是素数
            for(int j=i;j<=M;j+=i) //素数的倍数的euler[j]一并改掉
                euler[j]=euler[j]/i*(i-1);          //euler[j]=euler[j]*(1-1/i)
    }
    /*for(int i=2;i<=10;++i)
        cout<>T;
    for(int k=1;k<=T;k++)
    {
        int n;
        cin>>n;
        long long ans=0;
        for(int i=1;i<=n;++i)
            cin>>a[i];
        sort(a+1,a+1+n);
        for(int i=1,j=2;i<=n&&j<=M;j++)
        {
            if(euler[j]>=a[i])
            {
                ans+=j;
                i++;
                j--;
            }
        }
        cout<<"Case "<

Sigma Function  知识点:规律

记得当时看了很多推导,后悔当时没有写博客的习惯。

题意:1~n中有多少个数,其因子和为偶数

规律:因子和为奇数的有:平方数以及平方数的二倍。总数减去为奇数的就是偶数的

智商不够就暴力打表找规律叭_(:з」∠)_

#include
#include
#include
using namespace std;
typedef long long LL;
int main()
{
    int T;
    cin>>T;
   for(int i=1;i<=T;++i)
   {
        LL n;
        cin>>n;
        LL ans=(LL)sqrt(n)+(LL)sqrt(n/2);  //中间不强制转换的话会wa
        printf("Case %d: %lld\n",i,n-ans);
   }
       return 0;
}

Leading and Trailing  知识点:快速模指数算法  指数问题转成对数求解

题意:求n^k的前三位和后三位

思路:后三位很好求,只要模1000就能求出,关键是前三位

前三位的求法是这样的,将n^k用科学计数法表示,那么n^k=a\times 10^x  n^k前三位即a\times 100取整

我们将a用10^y表示,由于科学计数法中a<10,所以y一定是小数,即n^k=10^{x+y} 

y=k1og(n)-x 

//A
#include
#include
#include
using namespace std;
typedef long long ll;
const int mod=1000;
ll quick_mod(ll a,ll k)
{
    if(k==0) return 1;
    ll ans=1;
    while(k)
    {
        if(k&1)
            ans=ans*a%mod;
        a=a*a%mod;
        k=k>>1;
    }
    return ans;
}
int main()
{
    int T;
    cin>>T;
    for(int kase=1;kase<=T;kase++)
    {

        ll n,k;
        cin>>n>>k;
        ll trail=quick_mod(n,k);  //只要模1000就能算出最后三位
       double x=k*log10(n)-(int)(k*log10(n));   //强制转换要加括号
       double tmp=pow(10,x);
       int lead=(int)(tmp*100);
        printf("Case %d: %d %03d\n",kase,lead,trail);
    }
    return 0;
}

  Goldbach`s Conjecture  知识点:素数打表

题意:给一个数n,问能找到多少素数对,其和是n

#include
#include
#include
using namespace std;
const int N=1e7+10;
bool tag[N];
int prime[1000000];
int cnt=0;
void getprime()
{

    memset(tag,false,sizeof(tag));
    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]=true;
            if(i%prime[j]==0)
                break;
        }

    }
}
int main()
{
    getprime();
    int T;
    cin>>T;
    for(int k=1;k<=T;++k)
    {
        int n;
        cin>>n;
        int ans=0;
        //cout<<"keke"<

G - Harmonic Number (II)   找规律

[Kuangbin带你飞]专题十四 数论(一)_第1张图片

用正确方法求h(n)

#include
#include
using namespace std;
typedef long long LL;
LL h(LL n)
{
    LL res=0;
    LL i=1;
   for(;i*(i+1)>T;
    for(int kase=1;kase<=T;kase++)
    {
        LL n;
        cin>>n;
        cout<<"Case "<

 

I - Harmonic Number

题意:求1 + 1/2 + 1/3 + 1/4 + 1/ 5 +...+ 1/ n

思路:有公式,记不住咋办?将数分组存储的思路

#include
#include
using namespace std;
const int N=1e8+10;
const int NN=1e8/40+10;
//每隔四十记录一次答案  不然空间不够,这个样子的话循环一次 1s 之后每个数进来最多循环39次
double a[NN];
void pre()
{
    double sum=0;
    for(int i=1;i<1e8+10;++i)
    {
        sum+=(1.0)/i;
        if(i%40==0) a[i/40]=sum;
    }
}
int main()
{
    //cout<>T;
    int cas=0;
    while(T--)
    {
        ++cas;
        int n;
        cin>>n;
        int x=n/40;                     //将数锁定在一个范围,机智
        double ans=a[x];
        for(int i=x*40+1;i<=n;++i)
            ans+=1.0/i;
         printf("Case %d: %.10f\n",cas,ans);
    }

    return 0;
}

J - Mysterious Bacteria

题意:x=b^p 给出x,求最大的p

x=p_1^{\alpha_1}p_2^{\alpha_2}...p_n^{\alpha_n} =(p_1^{\alpha_1/y}p_2^{\alpha_2/y}...p_n^{\alpha_n/y})^y  这个y的最大值就是答案

显然y=gcd(\alpha_1,\alpha_2...\alpha_n)的时候最大

这题的坑点在于x可能是负的,我们在处理的时候把它转成正数去做的,如果y是偶数就会导致最终解为正数,所以要把一直除二,除到奇数为止

#include
#include
#include
using namespace std;
typedef long long LL;
const int N=1e6+100;
bool tag[N];
int prime[N];
int cnt;
void getprime()
{
    memset(tag,0,sizeof(tag));
    tag[0]=tag[1]=1;
    for(int i=2;i>1;
        }
    }
    return ans;

}
int main()
{
    getprime();
    int T;
    cin>>T;
    for(int kase=1;kase<=T;++kase)
    {
        LL x;   //不用LL的话会爆int有符号最大(2^16)
        cin>>x;
        cout<<"Case "<

K - Large Division

题意:b是32位有符号整数,判读a是否能整除b

数论的魅力在于:本来以为这道题必须要用大数来做了,but! (a+b)\%p=(a\%p+b\%p)\%p

如果a<0,先把a转化为绝对值

#include
#include
using namespace std;
typedef long long LL;
int main()
{
    int T;
    cin>>T;
    for(int kase=1;kase<=T;++kase)
    {
        string a;
        LL b,ans=0;
        cin>>a>>b;
        if(b<0) b=-b;
        int len=a.size();
        int i=0;
        if(a[0]!='-')
        {
             ans=a[0]-'0';
             i=1;
        }
        else
        {
            ans=a[1]-'0';
            i=2;
        }

        for(;i

M - Help Hanzo

题意:[a,b]区间内有多少素数  32位数10000以内区间长度判断素数个数

思路:数量太大无法打表,策略是把[a,b]区间->[0,b-a]区间,用素数筛在这个区间范围内筛素数

#include
#include
#include
using namespace std;
const int N=1e6+10;
const int M=2e6;
typedef long long LL;
int tag[N];
LL prime[M];
int interval[100010];
int cnt;
LL a,b;
void getp()
{
    memset(tag,0,sizeof(tag));
    tag[0]=tag[1]=1;
    for(int i=2;ib) break;   //用所有平方值小于b的prime去更新那个区间
        LL s=(a+prime[j]-1)/prime[j];          //计算第一个比a大的prime[j]的倍数是几倍  (向上取整的好操作)
        if(s<2) s=2;                           //避免把这个素数当成合数筛掉
        s*=prime[j];
        for(;s<=b;s+=prime[j])
            interval[s-a]=1;                    //把集合整到[0,b-a]
    }
    int ans=0;
    for(int i=a;i<=b;++i)
    {
        if(!interval[i-a])
            ++ans;
    }
    return ans;

}
int main()
{
    getp();
    int T;
    cin>>T;
    int kase=0;
    while(T--)
    {
        ++kase;
        cin>>a>>b;
        int ans=solve(a,b);
        if(a==1) --ans; //a=1的话在interval里面没有被筛掉
        printf("Case %d: %d\n",kase,ans);
    }
    return 0;
}

N - Trailing Zeroes (III)

题意:给出q,找出n,使得n!的十进制表达最后有q个0

题目的思路很简单,首先n!里面2和5搭配形成10,5的数目比2多,所以有几个5就能有几个10

我记得我是想暴力求出所有情况(打表),但是忽略了q是1e8的量级,这种涉及很大很大的数要用二分法

#include
#include
using namespace std;
const int N=1e8+10;
const int INF=0x3f3f3f3f;
typedef long long LL;
int Q;
LL solve(LL n)  //计算n!里有几个5
{
    LL res=0;
    while(n)
    {
        res+=n/5;           //如果是算n里面有几个5,每n/=5 加1 ,注意这个区别
        n/=5;
    }
    return res;
}

LL erfen(LL l,LL r)
{
    LL res=-1;
    while(l<=r)
    {
        LL mid=(l+r)/2;
        if(solve(mid)==Q)
        {
            res=mid;
            r=mid-1;
        }
        else if(solve(mid)>Q)
            r=mid-1;
        else
            l=mid+1;
    }
    return res;
}
int main()
{
   // cout<>T;
    for(int kase=1;kase<=T;++kase)
    {
        cin>>Q;
        LL left=1,right=INF;
        LL ans=erfen(left,right);
        if(ans==-1)
            cout<<"Case "<

O - GCD - Extreme (II)

题意:求出1~n之间所有数对的最大公约数之和

脑子抽了才会想去暴力。。。

f(n)=gcd(1,n)+gcd(2,n)+...+gcd(n-1,n)

s(n)=f(2)+f(3)+...+f(n-1)+f(n)=s(n-1)+f(n)

得到这个递推式后,问题转化为如何求f(n),当然是暴力遍历啦

g(n,i)表示满足gcd(x,n)=i的个数,则f(n)=\sum (i*g(n,i))

gcd(x/i,n/i)=1,所以与n的最大公约数是i的数有\phi(n/i)个,因此呼啦!最终就变成求欧拉函数了

这道题以及以往做过的数论题提醒我,筛法的思想很重要,特征是一个数由其所有约数更新,用筛法最省时(这种好像叫填表法)

#include
#include
#include
using namespace std;
const int N=4e6+10;
typedef long long LL;
int euler[N];
LL F[N];
LL G[N];
void geteu()
{
    for(int i=1;i

 

你可能感兴趣的:(数论)