约数和欧拉函数

约数个数问题:
1.1-n中的约数个数:
1.怎么求个数:分解质因子后
(c1+1)(c2+1)...
2.估算复杂度:1-n当中有每个数中的约数个数和
N/1+N/2+.. 总共是nlogn个
3.0-2e9 最多的约数个数是1600个数

T2

  • 数论题目本身就需要对式子进行一定的推导
  • 对于二元问题,我们转化成y=f(x)的形式
  • 进而问题变成:对于多少个x,有等式成立并y满足题目条件(正,整数)
  • 根据枚举的思路,分子分母中只出现一个x才好枚举
  • 如果最后缺乏条件,不妨回到原本的式子当中寻找
  • 最后得出是求n!^2的约束

求n!的约数个数:

  • 对于求这个,转化思想,变成每一个质数对于他的倍数有多大的贡献(乘法容易取模难)
  • 每次从n开始,寻找一共有多少个数是含有k个p的
  • 核心代码:for(int j=n;j;j/=p) s+=j/p;

T3
转化思路:约数个数最多的最小的数

  • 1.不同的质因子的个数数很小的<9
  • 2.每个质因子的次数小于30
  • 3.所有质因子的次数是递减的:小的先填
  • 数论优先思想【可以手算来先确定一下数据范围】
  • 2147483647(2^32-1)
#include <stdio.h>
#include <algorithm>
#include <cstring>

using namespace std;
#define ll long long
int prime[9]={2,3,5,7,11,13,17,19,23};
int n;
int maxd,num;

inline void dfs(int j,int ci,int p,int yue)
{
    if(yue>maxd||(yue==maxd&&p<num))
    {
        num=p;
        maxd=yue;
    }
    if(j==9) return;//
    for(int i=1;i<=ci;i++)
    {
        if((ll)p*prime[j]>n) break;//此处考虑爆int 开ll
        p*=prime[j];
        dfs(j+1,i,p,yue*(i+1));
    }
    return;
}
int main()
{
    scanf("%d",&n);
    dfs(0,30,1,1);//从1开始
    printf("%d",num);
    return 0;
}
View Code

注意一下爆搜的写法:

  • 1.明确搜索顺序(质数从小到大)->搜索边界(u==9)
  • 明确含参数:上一个的次数,当前枚举的个数,当前的乘积(防止爆炸)(prime*p>n),当前约数个数
  • 更新答案:(yue>maxd||yue==maxd&&p<num)
  • 更新过程:枚举次数 1->ci就

T4hanson的趣味题

  • (a,x)=b [c,x]=d求1-n当中有多少的n是满足的
  • 分析题目,二元条件转验证,枚举x需要找范围
  • 发现x是d的约数,所以转为找d的约数
  • 发现枚举需要根号n;
  • 根据思想:质因子的个数是很少的(x/lnx)所以考虑分解质因子,优化10倍
  •  

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int maxn=50000;
    typedef long long ll;
    int prime[maxn],cnt;
    bool st[maxn];
    
    struct Factor
    {
        int p,s;
    }f[maxn];
    int fcnt;
    int dive[maxn],dcnt;
    
    
    inline void init(int n)
    {
        for(int i=2;i<=n;i++)
        {
            if(!st[i]) prime[cnt++]=i;
            for(int j=0;i*prime[j]<=n;j++)
            {
                st[i*prime[j]]=true;
                if(i%prime[j]==0) break;
            }
        }
    }
    inline int gcd(int a,int b)
    {
        if(b==0 )return a;
        else return gcd(b,a%b);
    }
    inline void dfs(int u,int p)
    {
        if(u==fcnt)
        {
            dive[dcnt++]=p;
            return;
        }
        for(int i=0;i<=f[u].s;i++)
        {
            dfs(u+1,p);
            p*=f[u].p;
        }
    }
    int main()
    {
        init(maxn);
        int n;
        scanf("%d",&n);
        while(n--)
        {
            int a,b,c,d;
            scanf("%d%d%d%d",&a,&b,&c,&d);
            int t=d;
            fcnt=0;
            for(int i=0;prime[i]<=t/prime[i];i++)
            {
                int p=prime[i];
                if(t%p==0)
                {
                    int s=0;
                    while(t%p==0) t/=p,s++;
                    f[fcnt++]={p,s};
                }
            }
            if(t>1) f[fcnt++]={t,1};
            dcnt=0;
            dfs(0,1);
            int res=0;
            for(int i=0;i<dcnt;i++)
            {
                int x=dive[i];
                if(gcd(a,x)==b&&(ll)c*x/gcd(c,x)==d) res++;
            }
            printf("%d\n",res);
        }
        return 0;
    }
    View Code

     

  • 核心思想:枚举出每一个质因子来计算贡献,在int类型里面约数数量最大也就是1600
  • 注意指数枚举范围是0-s

二.欧拉函数:
T1可见的点

  • 分析题意:发现是摆在二维坐标系上的点
  • 换种说法,也就是每个直线y=kx上面的第一个整数对的点
  • 问题转化为求多少个实数对满足
  • 根据"找性质“原则,第一个的满足要求也就是 (x0,y0)互质【否则就可以写成n*x0,n*y0的形式】
  • 由此转化成求1-n有多少个互质的数
  • 发现和欧拉函数性质很像,考虑向其靠拢
  • 考虑"对称,特殊,旋转“减少维度的原则,发现只需要求一边
  • 问题得解
  • 代码如下
#include <stdio.h>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn=1010;

int prime[maxn],cnt;
bool st[maxn];
int phi[maxn];

inline void init(int n)
{
    phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!st[i])
        {
            phi[i]=i-1;
            prime[cnt++]=i;
        }
        for(int j=0;i*prime[j]<=n;j++)
        {
            st[prime[j]*i]=true;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];//(˵Ã÷֮ǰÒѾ­±»É¸¹ý£¬²»ÊÇ×îСµÄÖÊÒò×Ó£¬Ö»ÐèÒªÔÙ³ËÉÏÖ¸Êý¾Í¿É 
                break;
            }
            phi[prime[j]*i]=phi[i]*(prime[j]-1);//ȡģÓÐÓ࣬˵Ã÷ ÒªÔÙ³ËÉÏpk(1-1/pk)Ò²¾ÍÊÇpk-1 
        }
    }
    
}
int main()
{
    init(maxn);
    int n,m;
    scanf("%d",&m);
    for(int t=1;t<=m;t++)
    {
        scanf("%d",&n);
        int res=0;
        for(int j=1;j<=n;j++) res+=2*phi[j];
        printf("%d %d %d\n",t,n,res+1);
    }
    return 0;
}
View Code
  • 注释补充:
  • 若i%prime[j]==0,说明之前已经被筛过,只需要乘p就可
  • 否则,p是其中一个质因子,需要乘p(1-1/p)也就是p-1
  • 总结经验【二维图形可以映射到坐标系上,然后数行结合,根据对称,旋转的性质向已知靠拢】

T2 最大公约数

#include <stdio.h>
#include <algorithm>
#include <cstring>

using namespace std;
typedef long long ll;
const int maxn=1e7+10;

int prime[maxn],cnt;
bool st[maxn];
int phi[maxn];
ll s[maxn];
inline void init(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i])
        {
            prime[cnt++]=i;
            phi[i]=i-1;
        }
        for(int j=0;i*prime[j]<=n;j++)
        {
            st[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
    for(int i=1;i<=n;i++) s[i]=s[i-1]+phi[i];
}
int main()
{
    int n;
    scanf("%d",&n);
    init(n);
    ll res=0;
    for(int i=0;i<cnt;i++)
    {
        res+= s[n/prime[i]]*2 + 1;
    }
    printf("%lld",res);
}
View Code
  • 判断:注意明显需要开ll
  • 分析题意:1e7的数据范围(x,y)=p等价于(x/p,y/p)=1【原则:等价变换】
  • 转化为对于每一个p求gcd=1(也就是互质)有多少个
  • 【框架细节原则】:检验一下优解需不需要对其进行优化或者补充
  • 转化为欧拉函数就可

 

posted @ 2020-10-04 08:41  ILH  阅读(572)  评论(0)    收藏  举报