zoukankan      html  css  js  c++  java
  • 欧拉筛,线性筛,洛谷P2158仪仗队

    题目

    首先我们先把题目分析一下。

    emmmm,这应该是一个找规律,应该可以打表,然后我们再分析一下图片,发现如果这个点可以被看到,那它的横坐标和纵坐标应该互质,而互质的条件就是它的横坐标和纵坐标的最大公约数为一,那这题的意思就变成了,在一个n * n的方格内寻找所有点的横坐标和纵坐标互质的点的个数。

    但是这样复杂度肯定是过不去的。打表时间花费也是很多的,所以我们需要找到加快速度的方法,就是用欧拉函数来加快速度,所以我们就要实现大的优化,我们先明确欧拉函数是个什么东西.

    欧拉函数

    (φ(x))表示在(1)(x - 1)中所有与x互质的数的个数。这个函数一般就叫做欧拉函数。这个函数还具有一些性质.

    1. 如果(x)是质数,那(φ(x)=x-1)。(质数的性质就是这样)
    2. (m)(n)互质,那(φ(n * m)= φ(n)* φ(m)).(可以根据乘法原理推出)
    3. (x)是质数时,那(φ (x^k)=(x-1)×x^{k-1})

    所以我们可以通过观察和推算发现,如果想求出结果,那就是对于图的每个横坐标,都记录他的欧拉函数的值,然后加起来就是最终结果。然后把结果乘2,因为纵坐标也需要进行一波这样的操作。最后再加上一(因为2,2这个点也算)

    那到底应该怎么筛使得欧拉函数能够很快的算出来呢。

    线性筛和欧拉筛

    这里我们就要引进两个算法——线性筛和欧拉筛。

    线性筛是指以线性的时间筛素数,欧拉筛是以线性的时间求出欧拉函数。

    而且他们之间有着异曲同工之妙。

    线性筛

    代码:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define maxn 100010 
    int prime[maxn];//表示第几个质数。
    bool vis[maxn] = {0, 1};//判断是否为质数,如果是则为0
    int tot;
    int main()
    {
        int n, m;
        scanf("%d", &n);
        for (int i = 2; i <= n; i++)
        {
            if (!vis[i])
                prime[++tot] = i;
            for (int j = 1; j <= tot, prime[j] * i <= n; j++)
            {
                vis[i * prime[j]] = 1;
                if (!(i % prime[j]))//如果i能被prime[j]整除的话 
                    break;
            }
        }
    }
    

    我们分析上面的代码,唯一可能难理解的地方就是(break)的那个判断了。这也是欧拉筛的精髓所在,如果已经超出了范围需要退出,这个自不必多说,但是,如果(i\%prime[j]==0)时,为啥就要退出呢

    原理:

    首先我们需要明白一些性质:

    1. 我们筛素数时应该从小到大筛,方便后面优化时间,所以源代码循环中(i)是从小到大筛的。

    2. 任何一个合数都可以表示为几个质数的乘积。所以我们想要在线性时间内筛素数的话,每个合数应该都被它的最小质因子筛去。

    3. (i\%prime[j]==0)时,则(i)(prime[j])的倍数,设(i)(prime[j]*k),如果继续向下筛的话,下一个要筛的数是(i*prime[j+1]=prime[j]*k*prime[j+1]);此时要筛的数即(i*prime[j+1])就已经被(prime[j])筛去了,而根据性质1,(prime[j])要比(i)小(因为(i)(prime[tot]))。

      因此时间复杂度是线性的。

    欧拉筛

    欧拉筛其实跟线性筛差不了多少。
    首先我们应该熟记欧拉函数的性质。并且这种筛法还可以进行线性求积性函数。

    原理

    1. 我们看线性筛的第二个性质,欧拉函数是不是也满足,因此防止多余的运算

    代码

        memset(isprime, 1, sizeof(isprime));
        isprime[1] = false;
        for (int i = 2; i <= listsize; i++)
        {
            if (isprime[i])
            {
                 prime[++primesize]  =i;
                 phi[i] = i - 1;//性质1
            }
            for (int j = 1; j <= primesize && i * prime[j] <= listsize; j++)
            {
                isprime[i * prime[j]] = false;
                if (i % prime[j] == 0)//说明他们之间不互质,且i是prime[j]的倍数,就可以用性质3.
                {
                    phi[i * prime[j]] = phi[i] * prime[j];
                    break;
                }
                phi[i * prime[j]] = phi[i] * (prime[j] - 1)//即phi[j];因为他们互质,所以可用性质1,2
            }
        }
    
  • 相关阅读:
    同样的代码bug
    Vim中的Tab
    在Vue中同时使用过渡和动画
    在Vue中使用animate.css
    Vue中的css动画
    动态组件与v-once指令
    在Vue中使用插槽
    非父子组件间的传值
    给组件绑定原生事件
    组件参数校验和非props特性
  • 原文地址:https://www.cnblogs.com/liuwenyao/p/9931230.html
Copyright © 2011-2022 走看看