首页 > 其他分享 >五一培训第二天

五一培训第二天

时间:2024-08-03 17:20:23浏览次数:10  
标签:10 le 培训 int 质数 --- 第二天 fac 五一

一 素数

今天先来回顾一下之前学过的素数(质数),当n是质数时,以下两个式子,至少有一个是成立的

1.a的d次方 % n == 1

2.存在一个i,0 <= i < r,a的d乘2的i次方的次方 % n == n - 1

那我们怎样用它判断素数呢?

如果\(n\)为质数 --- \(a\) --- 一定成立

如果\(n\)为合数 --- \(a\) --- 可能成立,可能不成立 --- 如果不成立,为合数 --- 如果成立,再算,再成立,再算 --- 如果一直成立,不保证100%,但是99.999% --- 要是错了,去买彩票吧...

总结就是,miller_rabin算法用了极其微小的正确率,换来了更加优秀的时间复杂度

#include<bits/stdc++.h>
using namespace std;

int ksm(int a,int b,int p){
    if(b == 0) return 1%p;
    int c=ksm(a,b/2,p);
    c=1ll*c*c%p;
    if(b % 2 == 1) c=1ll*c*a%p;
    return c;
}

bool miller_rabin(int n,int a){//O(log n)
    int d=n-1,r=0;
    while(d % 2 == 0){
        d=d/2;
        r=r+1;
    }
    int v=ksm(a,d,n);
    if(v == 1) return true;//若第一条满足,return ture
    for(int i=0;i<r;i++){
        if(v == n-1) return true;//若第二条满足,return true
        v=(__int128)v*v%n;//没有必要写快速幂,不断的平方就好了
    }
    return false;
}

bool is_prime(int n){
    if(n < 2) return false;
    if(n == 2) return true;
    for(int i=1;i<=20;i++){
        int a=rand() % (n-1)+1;//赌徒,随机赋值
        if(miller_rabin(n,a) == false) return false;
    }
    return true;
}

int prime_list[10]={2,3,5,7,11,13,17,19,31,97};

bool is_prime2(int n){
    if(n < 2) return false;
    for(int i=0;i<10;i++){
        if(n == prime_list[i]) return true;
        if(n % prime_list[i] == 0) return false;
        if(miller_rabin(n,prime_list[i] % n) == false) return false;
    }
    return true;
}

signed main(){
    int n;
    cin>>n;

    if(is_prime(n) == true) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;

    if(is_prime2(n) == true) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;

    return 0;
}

那如果换一种形式呢?现在,请你求出1$n$中的所有质数。可以将枚举1\(n\),将i的所有倍数都标记为不是质数,你肯定会写出这样的一坨代码

int not_prime[1010];
void f(int n){//O(nloglog n)
    for(int i=2;i<=n;i++){
        if(not_prime[i] == false){
            for(int j=i+i;j<=n;j+=i){
                not_prime[j] = true;
            }
        }
    }
}

可以看出,它的复杂度是O(nloglog n),其实已经够用了,但我们还要继续优化——线性筛

#include<bits/stdc++.h>
using namespace std;wq

const int maxn=1000010;

int pcnt,not_prime[maxn],plist[maxn];

signed main(){
    int n;
    cin>>n;
    not_prime[1] = true;

    for(int i=1;i<=n;i++){
        if(not_prime[i] == false){
            pcnt=pcnt+1;
            plist[pcnt] = i;
        }

        for(int j=1;j<=pcnt;j++){
            int v=plist[j] * i;
            if(v > n) break; 
            not_prime[v] = true;
            if(i % plist[j] == 0){
                break;
            }
        }
    }
    return 0;
}

二 组合数学

组合数学主要分为加法原理乘法原理,他们两个的区别就是加法原理不可以同时选,而乘法原理可以同时选,具体看图

现在给你一道题,有n个人,从中选出m个人,而不同的顺序算不同的方案

可得出\(P(n,m)=n(n-1)(n-2)(n-3)(n-4)....(n-m)\)

很好,那如果不同的顺序算相同的方案呢

可得出\(\dfrac{P(n,m)}{m!}\)

相信聪明的你肯定还可以列出组合数的递推式

\(C(i,j) = C(i-1,j-1) + C(i-1,j)\)

有没有感觉这坨东西有点似曾相识,没错,这就是杨辉三角形,看下面

1
1 1
1 2 1 
1 3 3 1
1 4 6 4 1

三 还是组合数学

在组合数学中,有一类很常见的题目,但他们又有一些其他的分类(待补充)

给定\(n,m,p\),请你按照各题输出\(C(n,m)\%P\)

  • \(n,m \le 10^3,P \le 10^9\):

    由于数据范围不大,所以直接用递推式\(C(i,j) = C(i-1,j-1) + C(i-1,j)\)

    signed main(){
        int t,k;
        cin>>t>>k;
    
        for(int i=0;i<=200;i++){
            c[i][0] = 1;
            for(int j=1;j<=i;j++){
                c[i][j]=c[i-1][j-1]+c[i-1][j];
            }
        }
    
        cout<<c[t][k]<<'\n';
        return 0;
    }
    
  • \(n,m \le 10^6,P \approx 10^9\)(\(P\) 为质数,有 \(q\) 次询问,\(q \le 10^6\)):

    可以用公式 \(C(n,m)= \dfrac{n!}{m!(n-m)!}\)。

    signed main(){
        fac[0] = 1;
        for(int i=1;i<=1000000;i++){
            fac[i]=1ll*fac[i-1]*i%p;
        }
        for(int i=0;i<=1000000;i++){
            ifac[i]=ksm(fac[i],p-2);
        }
    
        for(int i=1;i<=q;i++){
            int n,m;
            cin>>n>>m;
            int ans=1ll*fac[n]*ifac[m]%p*ifac[n-m]%p;
            cout<<ans<<endl;
        }
        returen 0;
    }
    
  • \(n \le 10^9,m \le 10^3,1 \le P \le 10^9\)(任意数)

    可以用程序来实现约分

    signed main(){
        cin>>n>>m>>p;
        for(int i=1;i<=m;i++){
            fenzi[i] = n-i+1;
            fenmu[i] = i;
        }
    
        for(int i=1;i<=m;i++){
            for(int j=1;j<=m;j++){
                int g=gcd(fenzi[i],fenmu[j]);
                fenzi[i] = fenzi[i] / g;
                fenmu[j] = fenmu[j] / g;
            }
        }
    
        int ans=1;
        for(int i=1;i<=m;i++){
            ans=1ll*ans*fenzi[i]%p;
        }
        cout<<ans<<endl;
    
        return 0;
    }
    
  • \(n,m \le 10^18,P \le 10^5,P\) 为质数,有 \(q\) 次询问(\(q \le 10^5\)):

    需要用到Lucas 定理(当 \(P\) 是质数时,$C(n,m) % P=C(n % P,m % P) \times C(n \div P,m \div P) % P $)。

    int C(int n,int m){
        if(n < p) return 1ll * fac[n] * ifac[m] % p *ifac[n-m] % p;
        else return 1ll * C(n%p,m%p) * C(n/p,m/p) % p;
    }
    signed main(){
        fac[0] = 1;
        for(int i=1;i<=1000000;i++){
            fac[i]=1ll*fac[i-1]*i%p;
        }
        for(int i=0;i<=1000000;i++){
            ifac[i]=ksm(fac[i],p-2);
        }
    
        for(int i=1;i<=q;i++){
            int n,m;
            cin>>n>>m;
            int ans=1ll*fac[n]*ifac[m]%p*ifac[n-m]%p;
            cout<<ans<<endl;
        }
        returen 0;
    }
    

四 抽屉原理

大概意思就是你先在有\(n+1\)个鼠标,还有\(n\)台电脑,把鼠标插到电脑上,那么肯定有一台电脑要插两个鼠标。这,就是抽屉原理

现在,给你一个数\(C\),并给你\(N\)个数,从他们之中选出任意个数,使得他们的和是\(C\)的倍数。

那么,这和抽屉原理有什么关系呢?

我们可以把它看成一个序列,每一个数的位置都有一个前缀和,也就是有\(n\)个前缀和,把这\(n\)个前缀和放到\(n-1\)个抽屉里,那一个放两个的那段序列,就是最终的答案!

五 容斥原理

要想了解他,先看一道题。

给你一个数\(n\),请问\(1-n\)中有多少个数可以表示成\(x ^ y,1 \le y\)

#include<bits/stdc++.h>
using namespace std;

int fac[70];
//fac[i] 代表 x^i这种数 被算了几次了 

long long n;

int main()
{
	cin >> n;//计算1-n中有多少个数可以表示成x^y,x>1,y>1的形式 
	long long ans=0;
	for (int i=2;i<=64;i++)
	{
		long long v = floor(pow(n,1.0/i)) - 1;//计算2~n中有多少个数 可以表示成x^i的形式 
		int d = 1 - fac[i];//代表x^y这种 还应该被算几次 
		ans += d*v;
		for (int j=i;j<=64;j+=i)
			fac[j] += d;
	}
	cout << ans+1 << endl;
	//输出时记得加上减去的 1
	return 0;
}

六 组合数拆分P4369

现在有这样一道例题,给定你\(x\)和\(k\),请你把\(x\)拆分成\(k\)个不同的组合数只和,请你输出任意一种方案。\(x \le 10^9 , k \le 10^3\)

#include <bits/stdc++.h>
using namespace std;

signed main(){
	int x,k;
	cin>>x>>k;
	
	for (int i=1;i<k;i++){
		cout<<i<<" "<<0<<'\n';
	}
	
	cout<<x-k+1<<" "<<1<<'\n';
	return 0;
}

标签:10,le,培训,int,质数,---,第二天,fac,五一
From: https://www.cnblogs.com/yantaiyzy2024/p/18340803

相关文章

  • Java计算机毕业设计教育培训系统设计与实现(开题报告+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在信息化高速发展的今天,教育领域正经历着前所未有的变革。传统的教学模式逐渐显露出其局限性,如资源分配不均、互动性不足、学习效率不高等问题日益凸......
  • Python数据结构第二天—循环链表、树、二叉搜索树
    双向链表之前学习的单向链表只能从头遍历到尾,过程是单向的,而双向链表既可以从头遍历到尾,也可以从尾遍历到头,它的过程是双向的。既然它是双向的,那么我们要实现一个双向链表,就需要在单向链表的基础上,给每一个结点增加一个向前的引用。双向链表的创建:"""我们要实现的是一......
  • (PSM) 认证培训课程:精通Scrum,提升项目管理技能
    ​在快速变化的商业环境中,高效的项目管理和团队协作是企业成功的关键。作为一种广泛认可的敏捷框架,Scrum已成为推动项目成功和提高团队效率的重要工具。为了帮助专业人士掌握Scrum方法和实践,Scrum.org推出了ProfessionalScrumMaster(PSM)官方认证班,专为希望提升项目管理能力......
  • Springboot 计算机毕业设计“爱艺创”特长培训管理系统程序
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表学生,教职工,班级信息,课程分类,课程信息,风采展示,学生档案,教职工档案开题报告内容一、研究背景与意义1.研究背景随着社会的发展和进步,特长培训在培养学......
  • 基于python电力安全员施工培训系统【源码+文档+PPT】
    精彩专栏推荐订阅:在下方主页......
  • java基础题(附答案)(第二天的知识点预热)
    一、填空题Java语言规定标识符由字母、下划线、美元符号和数字组成,并且第一个字符不能是数字。Java中整型变量有byte、short、int和long四种,不同类型的整数变量在内存中分配的字节数不同,数值范围也不同。对于int型变量,内存分配4个字节。在Java中浮点型变量有fl......
  • 企业级Scrum敏捷开发培训:推动团队高效运作
    ​在当今快速变化的商业环境中,企业必须不断创新和快速响应市场需求,以保持竞争优势。Scrum敏捷开发方法作为一种高效的项目管理框架,已被全球众多企业采用,用于提高团队协作和交付速度。为了帮助企业更好地理解和应用Scrum,我们推出了Scrum敏捷开发企业级实训课程,旨在提升团队效率,推......
  • 自学java第二天
    String类型的基本使用String是引用数据类型,变量定义的格式为:String变量名="";""中的内容可以是任意的,叫做字符串,与char不同,char类型叫做字符,里面只能有一个内容。String的运算规则,在和基本数据类型进行运算时,会进行拼接的操作。例如:publicclassindex{publicst......
  • JavaWeb第二天
    目录tlias案例实践登录校验1,Cookie技术——存储在客户端2,Session技术——存储在服务端3,令牌Token技术JWT(JSONWebToken)令牌4,过滤器Filter定义过滤器Filter拦截路径过滤器链5、拦截器interceptor6、全局异常处理器7、Spring事务管理事务进阶——事务属性AOP——面向切面编......
  • 网络安全基础知识及安全意识培训(73页可编辑PPT)
    引言:在当今数字化时代,网络安全已成为企业和个人不可忽视的重要议题。随着互联网的普及和技术的飞速发展,网络威胁日益复杂多变,从简单的病毒传播到高级持续性威胁(APT)、勒索软件攻击、数据泄露等,无一不对网络安全构成了严峻挑战。因此,开展网络安全基础知识及安全意识培训,对于提升......