川大新生杯——欧拉筛

佐手、 提交于 2019-12-05 20:33:56

mod的一些性质

(a mod n) (b mod n)=(ab) mod n

((a mod n)(b mod n)) mod n=(ab) mod n

如果a≡b(mod m),x≡y(mod m),则a+x≡b+y(mod m)。

如果a≡b(mod m),x≡y(mod m),则ax≡by(mod m)。

如果ac≡bc(mod m),且c和m互质,则a≡b(mod m) (就是说同余式两边可以同时除以一个和模数互质的数)。

川大新生杯——模板筛

lglg最近爱上了炒股,他想在股票市场内买一些股票。

现在有编号为1,2,3,...,100000000的100000000支股票,每个股票的价值等于其编号的阶乘(例如编号为5的股票的价值就是120)。

lglg是一个很挑剔的人,他只喜欢编号为质数的股票,但他很有钱,因此他希望买下所有编号小于等于N(1<=N<=1e8)并且编号为质数的股票,请你帮他算一算这些股票的价值之和。

由于价值和很大,他希望你能告诉他价值之和对P(1e3<=P<=1e5)取模后的值。

输入描述:

输入包含多组数据 第一行是一个正整数 T(1<=T<=1000),代表数据的组数。每组数据只有一行,包含两个正整数 N 和 P 数字之间用空格隔开,其代表的含义及范围已在题面中给出。

输出描述:

每组数据输出一行,表示lglg希望买下的所有股票的价值之和对P取模后的值

示例1

输入

2
5 1001 
20 1001  

输出

128
86 

欧拉筛和mod的运用能让跑起来贼快。

mod:

1、阶乘的时候比mod的数大了肯定就永远是零了,不用算了,所以比P大的肯定是零了,阶乘和对p取余,那阶乘对P取余岂不是更满足了,所以欧拉筛只要筛1e5以下的就行,不用1e8

2、每个组数据只算算一遍阶乘,遇到素数就算和,不是素数不算,但阶乘(和mod)还在跑,循环结尾到n,要是到mod的p了也不用跑了,后面必定已经都零了

欧拉筛:打表从2筛到需要的最大,每个合数被它的最小质因子筛出去,prime【0】用来存储一共多少,对于visit[i*prime[j]] = 1 的解释: 这里不是用i的倍数来消去合数,而是把 prime里面纪录的素数,升序来当做要消去合数的最小素因子。
打表观察来理解 :img

发现i在消去合数中的作用是当做倍数的。
对于 i%prime[j] == 0 就break的解释 :当 i是prime[j]的倍数时,i = kprime[j],如果继续运算 j+1,i * prime[j+1] = prime[j] * k prime[j+1],这里prime[j]是最小的素因子,当i = k * prime[j+1]时会重复,所以才跳出循环。
举个例子 :i = 8 ,j = 1,prime[j] = 2,如果不跳出循环,prime[j+1] = 3,8 * 3 = 2 * 4 * 3 = 2 * 12,在i = 12时会计算。因为欧拉筛法的原理便是通过最小素因子来消除。
————————————————
版权声明:本文为CSDN博主「彤云望月」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39763472/article/details/82428602

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
bool visit[100010];
int prime[100010];
void oulashai()
{
    memset(visit,0,sizeof(visit));
    for (int i=2;i<=100000;i++)
    {
        if (!visit[i])
          prime[++prime[0]]=i; 
        for (int j=1;j<=prime[0] and i*prime[j]<=100000;j++)
        {
            visit[i*prime[j]]=1;
            if (i%prime[j]==0)
              break;
        }
    }
}

int main()
{
  //  freopen("test.in","r",stdin);
 //     freopen("test.out","w",stdout);
    int t,n,p;
    oulashai();
    cin>>t;
    for (int i=1;i<=t;i++)
    {
        long long sum=0,jc=1;
        scanf("%d%d",&n,&p);
        for (int j=2;j<=min(n,p);j++)
        {
            jc=(jc*j)%p;
            if (!visit[j])
              sum=(sum+jc)%p;
        }
        printf("%lld\n",sum);
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!