week5-质数-最大公约数-快速幂-组合计数-博弈论

文章介绍了与等差数列相关的欧几里得算法应用,包括如何找到包含特定整数的最短等差数列。此外,还讨论了质数的相关算法,如试除法判断质数、埃氏筛法和线性筛法筛质数,以及质数距离问题。同时,提到了快速幂运算及其在求幂次方和逆元中的应用。
摘要由CSDN通过智能技术生成

等差数列——欧几里得算法

数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一部分的数列,只记得其中 N 个整数。
现在给出这 N 个整数,小明想知道包含这 N 个整数的最短的等差数列有几项?
输入格式
输入的第一行包含一个整数 N。
第二行包含 N 个整数 A1,A2,⋅⋅⋅,AN。(注意 A1∼AN 并不一定是按等差数列中的顺序给出)
输出格式
输出一个整数表示答案。
数据范围
2≤N≤100000,
0≤Ai≤109
输入样例:
5
2 6 4 10 20
输出样例:
10
样例解释
包含 2、6、4、10、20
的最短的等差数列是 2、4、6、8、10、12、14、16、18、20

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N=1e5+10;
int a[N];
int gcd(int a ,int b)
{
	return b? gcd(b,a%b):a;
}
int main()
{
	int n;
	scanf("%d",&n);;
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	sort(a,a+n);
	int d=0;
	for(int i=1;i<n;i++) d=gcd(d,a[i]-a[0]);
	if(!d)   printf("%d\n",n);
	else printf("%d\n",(a[n-1]-a[0])/d+1);
}

质数

在大于1的整数中,如果只包含1和本身两个约数

质数的判定——试除法

时间复杂度:o(sqrt(n))

bool isprime(int n)
{
if(n<2)  return false;    //不要漏
for(int i=2;i<=n/i;i++)
  if(n%i==0)
    return false;
 return true;
 }

分解质因数——试除法

o(sqrt(n))

void divide(int x)
{
  for(int i=2;i<=n/i;i++)
    if(n%i==0)   //i一定是质数
    {
       int s=0;
        while(n%i==0)
        {
          n/=i;
          s++;
       }
      printf("%d %d\n",i,s);
    }
    if(n>1) printf("%d %d\n",n,1);
    puts("");
}

筛质数——埃氏筛法

求1~n中的质数个数
输入
8
输出
4

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000010;
int primes[N],cnt;
bool st[N];
void get_primes(int n)
{
	for(int i=2;i<=n;i++)
	{
		if(!st[i]) 
		{	
		 primes[cnt++]=n; //没有被筛过
		for(int j=i+i;j<=n;j=j+i)  st[j]=true;//放里面,把每个质数的倍数删掉
		}
		// for( int j=i+i;j<=n;j=j+i)  st[j]=true;//每个数的倍数删掉
	}
}
int main()
{
	int n;
	cin>>n;
	get_primes(n);
	cout<<cnt<<endl;
}

筛质数——线性筛法

n只会被它的最小质因子筛掉

//其余部分同上
void get_primes(int n)
{
	for(int i=2;i<=n;i++)
	{
	   if(!st[i])  primes[cnt++]=i;
	   for(int j=0;primes[j]<=n/i;j++) //从小到大枚举所有质数
	   {
	   
	   	st[primes[j]*i]=true;
	   	if(i%primes[j]==0)  break;  //primes[j]一定是i的最小质因子,primes[j]一定是primes[j]*i的最小质因子
	   	//i%primes[j]!=0,primes[j]一定是primes[j]*i的最小质因子
	   }
	}
}

质数问题

给定两个整数 n 和 k,请你判断在 [2,n] 的范围内是否存在不少于 k 个质数,满足可以表示为两个相邻质数与 1 的和。
例如,19 满足条件,因为 19=7+11+1。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据占一行,包含两个整数 n 和 k。
输出格式
每组数据输出占一行,如果存在不少于 k 个质数满足条件则输出 YES,否则输出 NO。
数据范围
1≤T≤30,
2≤n≤1000,
0≤k≤1000
输入样例:
5
27 2
45 7
2 0
15 1
17 1
输出样例:
YES
NO
YES
YES
YES

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N=1010;
int primes[N],cnt;
bool st[N];

void get_primes(int n)
{
	for(int i=2;i<=n;i++)
	{
		if(!st[i])  primes[cnt++]=i;
		for(int j=0;primes[j]<=n/i;j++)
		{
			st[primes[j]*i]=true;
			if(i%primes[j]==0)  break;
		}
	}
}

int main()
{
	get_primes(N-1);
	int t;
	cin>>t;
	while(t--)
	{
		int n,k;
		cin>>n>>k;
		int res=0;
		for(int i=2;i<=n;i++)
		{
			if(st[i]) continue;
			for(int j=1;j<cnt;j++)
			  if(primes[j-1]+primes[j]+1==i)
			  {
			  	res++;
			  	cout<<j-1<<' '<<j<<endl; 
			  	break;
			  }
		}
		if(res>=k)  puts("YES");
		else puts("NO");
 }
}

质数距离

约数

试除法求约数

输入
2
6
8
输出
1 2 3 6
1 2 4 8

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

vector<int> get_divisors(int n)
{
	vector<int> res;
	for(int i=1;i<=n/i;i++)
	if(n%i==0)
	{
		res.push_back(i);
		if(i!=n/i)  res.push_back(n/i); //边界特判
	}
	sort(res.begin(),res.end());
	return res;
}
int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		int x;
		cin>>x;
		auto res=get_divisors(x);
		for(auto t:res)  cout<<t<<" ";
		cout<<endl; 
	}
	return 0;
}

约数个数

N = P 1 a 1 P 2 a 2 P 3 a 3 . . . P k a k N=P_1^{a_1}P_2^{a_2}P_3^{a_3}...P_k^{a_k} N=P1a1P2a2P3a3...Pkak
**约数个数 **(0~)
( a 1 + 1 ) ( a 2 + 1 ) ( a 3 + 1 ) . . . ( a k + 1 ) (a_1+1)(a_2+1)(a_3+1)...(a_k+1) (a1+1)(a2+1)(a3+1)...(ak+1)
int 范围内个数最多一千五百多个

给定n个正整数 a i a_i ai,请你输出这些数的乘积的约数个数
输入
3
2
6
8
输出
12

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int main()
{
	int n;
	cin>>n;
	//定义哈希表
	unordered_map<int,int> primes; 
	while(n--)
	{
		int x;
		cin>>x;
		for(int i=2;i<=x/i;i++)
		while(x%i==0)
		{
			x/=i;
			primes[i]++;
		}
		if(x>1)  primes[x]++;
	}
	LL res=1;
	for(auto prime:primes)  res=res*(prime.second+1)%mod;
	cout<<res<<endl;
}

约数之和

约数之和
( P 1 0 + P 1 1 + P 1 2 . . . P 1 a 1 ) . . . ( P k 0 + P k 1 + . . . P k a k ) (P_1^0+P_1^1+P_1^2...P_1^{a_1})...(P_k^0+P_k^1+...P_k^{a_k}) P10+P11+P12...P1a1...(Pk0+Pk1+...Pkak)
输入
3
2
6
8
输出
252

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int main()
{
	int n;
	cin>>n;
	//定义哈希表
	unordered_map<int,int> primes; 
	while(n--)
	{
		int x;
		cin>>x;
		for(int i=2;i<=x/i;i++)
		while(x%i==0)
		{
			x/=i;
			primes[i]++;
		}
		if(x>1)  primes[x]++;
	}
	LL res=1;
	for(auto prime:primes)  
	{
		int p=prime.first,a=prime.second;
		LL t=1;
		while(a--) t=(t*p+1)%mod;
		res=res*t%mod;	
	}
	cout<<res<<endl;
}

最大公约数-欧几里得算法(辗转相除法)

原理:(a,b)=(b,a mod b)
输入
2
3 6
4 6
输出
3
2

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

int gcd(int a,int b)
{
//b不是0返回gcd(b,a%b),b是0返回a (0可以整除任何数) 
	return b? gcd(b,a%b):a;
}
int main()
{
	int n;
	cin>>n;

    while(n--)
  {
	int a,b;
	cin>>a>>b;
	printf("%d\n",gcd(a,b));
  }
  return 0;
}

扩展欧几里得算法

裴蜀定理

对任意正整数a,b,那么一定存在非零整数x,y使得ax+by=(a,b)
输入
2
4 6
8 18
输出
-1 1
-2 1

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
	if(!b) 
	{
	  x=1,y=0;
	  return a;
	 }                        //a mod b=a-a/b*b
	 int d= exgcd(b,a%b,y,x);//by+(a mod b)x=d
	 y=y-a/b*x;
	 return d;
}
int main()
{
	int n;
	scanf("%d",&n);
	
	while(n--)
	{
		int a,b,x,y;
		scanf("%d%d",&a,&b);
		exgcd(a,b,x,y);
		printf("%d %d\n",x,y);
	}
	
 } 

应用——线性同余方程

第一行包含整数n,接下来n行,每行包含一组数据 a i , b i , m i a_i,b_i,m_i ai,bi,mi
a i ∗ x i = b i m o d m i a_i*x _i= b_i mod m_i aixi=bimodmi
存在y,使得ax=my+b => ax+my’=d(两边乘d/b)
(a,m)|b则有解,即只要d是b的倍数一定有解
输出一个整数表示满足条件的 x i x_i xi,无解则输出impossible
输入
2
2 3 6
4 3 5
输出
impossible
-3

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int exgcd(int a,int b,int &x,int &y)
{
	if(!b) 
	{
	  x=1,y=0;
	  return a;
	 } 
	 int d= exgcd(b,a%b,y,x);
	 y=y-a/b*x;
	 return d;
}
int main()
{
	int n;
	scanf("%d",&n);
	
	while(n--)
	{
		int a,b,m;
		scanf("%d%d%d",&a,&b,&m);
		int x,y;
		int d=exgcd(a,m,x,y);
	  if(b%d) puts("impossible");
	else	printf("%d\n",(LL)x*(b/d)%m);
	}
	
 } 

消灭老鼠

Hankson的趣味问题

欧拉函数

公式法

在这里插入图片描述

给定n个正整数 a i a_i ai,求出每个数的欧拉函数
输入
3
3
6
8
输出
2
2
4

#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		int a;
		cin>>a;
		
		int res=a;
		for(int i=2;i<=a/i;i++) //分解质因数
		 if(a%i==0)
		{
		   //res=res*(1-1/i);
			res=res/i*(i-1);
			while(a%i==0)  a/=i;
		}
		if(a>1) res=res/a*(a-1);
		cout<<res<<endl;
	}
	return 0;
}

筛法

1~N中
输入
6
输出
12

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1000010;

int primes[N],cnt;
int phi[N];
bool st[N];
LL get_eulers(int n)
{
	phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!st[i])
		{
			primes[cnt++]=i;
			phi[i]=i-1;
		}
		for(int j=0;primes[j]<=n/i;j++)
		{
			st[primes[j]*i]=true;
			if(i%primes[j]==0)
			{
				phi[primes[j]*i]=primes[j]*phi[i];
				break;
			  }  
			  phi[primes[j]*i]= (primes[j]-1)*phi[i];
		}
	}
	LL res=0;
	for(int i=1;i<=n;i++)  res+=phi[i];
	return res;
}
int main()
{
	int n;
	cin>>n;
	
	cout<<get_eulers(n);
}

快速幂

快速幂

给定n组 a i , b i , p i a_i,b_i,p_i ai,bi,pi,对于每组数据,求出 a i b i m o d p i a_i^{b_i}modp_i aibimodpi的值
时间复杂度:O(log k)

a k = a 2 x 1 ∗ a 2 x 2 ∗ a 2 x 3 . . . a 2 x t a^k=a^{2^{x_1}}*a^{2^{x_2}}*a^{2^{x_3}}...a^{2^{x_t}} ak=a2x1a2x2a2x3...a2xt
输入
2
3 2 5
4 3 9
输出
4
1

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int n;

//a^k%p
int qmi(int a,int k,int p)
{
	int res=1;
	while(k)
	{
		if(k&1)  res=(LL)res*a%p;
		k=k>>1;
		a=(LL)a*a%p;
	}
	return res;
}
int main()
{
	scanf("%d",&n);
	while(n--)
	{
		int a,k,p;
		scanf("%d%d%d",&a,&k,&p);
		printf("%d\n",qmi(a,k,p));
	}
}

幂次方

快速幂求逆元

给定n组 a i , p i a_i,p_i ai,pi,其中 p i p_i pi是质数,求 a i a_i ai p i p_i pi的乘法逆元,若逆元不存在,则输出impossible
费马定理(b和p互质):
在这里插入图片描述
输入
3
4 3
8 5
6 3
输出
1
2
impossible

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int n;

//a^k%p
int qmi(int a,int k,int p)
{
	int res=1;
	while(k)
	{
		if(k&1)  res=(LL)res*a%p;
		k=k>>1;
		a=(LL)a*a%p;
	}
	return res;
}
int main()
{
	scanf("%d",&n);
	while(n--)
	{
		int a,k,p;
		scanf("%d%d",&a,&p);
		int res=qmi(a,p-2,p);
		if(a%p)	printf("%d\n",res);
		else puts("impossible");
	}
}

中国剩余定理

高斯消元

组合计数

求组合数I

C a b = C a − 1 b + C a − 1 b − 1 C_a^b=C_{a-1}^b+C_{a-1}^{b-1} Cab=Ca1b+Ca1b1
输入
3
3 1
5 3
2 2
输出
3
10
1

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2010,mod=1e9+7;
int c[N][N];
//预处理,递推
int init()
{
	for(int i=0;i<N;i++)
	  for(int j=0;j<=i;j++)
	   if(!j)  c[i][j]=1;
	   else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
int main()
{
	init();
	int n;
	cin>>n;
	while(n--)
	{
		int a,b;
		cin>>a>>b;
		cout<<c[a][b]<<endl;
	}
}

求组合数II

C a b = a ! b ! ( a − b ) ! C_a^b=\frac{a!}{b!(a-b)!} Cab=b!(ab)!a!
输入
3
3 1
5 3
2 2
输出
3
10
1

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N=100010,mod=1e9+7;
typedef long long LL;
int fact[N],infact[N];
int qmi(int a,int k,int p)
{
	int res=1;
	while(k)
	{
		if(k&1) res=(LL)res*a%p;
		a=(LL)a*a%p;
		k>>=1;
	}
	return res;
}
int main()
{
	fact[0]=infact[0]=1;
	for(int i=1;i<N;i++)
	{
		fact[i]=(LL)fact[i-1]*i%mod;
		infact[i]=(LL)infact[i-1]*qmi(i,mod-2,mod)%mod;
	}
	int n;
	cin>>n;
	while(n--)
	{
		int a,b;
		cin>>a>>b;
		printf("%d\n",(LL)fact[a]*infact[b]%mod*infact[a-b]%mod);
	}
	
}

求组合数III

C a b = C a m o d p b m o d p ∗ C a / p b / p C_a^b=C_{a mod p}^{b mod p}*C_{a/p}^{b/p} Cab=CamodpbmodpCa/pb/p(mod p)
输入
(a,b,p)
5 3 7
3 1 5
6 4 13
输出
3
3
2
20组询问,询问范围1~1e18

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long LL;
int p;
int qmi(int a,int k)
{
	int res=1;
	while(k)
	{
		if(k&1)  res=(LL)res*a%p;
		a=(LL)a*a%p;
		k>>=1;
	}
	return res;
}
int C(int a,int b)
{
	int res=1;
	for(int i=1,j=a;i<=b;i++,j--)
	{
		res=(LL)res*j%p;
		res=(LL)res*qmi(i,p-2)%p;
		
	}
	return res;
}
int lucas(LL a,LL b)
{
	if(a<p&&b<p)  return C(a,b);
	return (LL)C(a%p,b%p)*lucas(a/p,b/p)%p;
}
int main()
{
	int  n;
	cin>>n;
	while(n--)
	{
		LL a,b;
		cin>>a>>b>>p;
		cout<<lucas(a,b)<<endl;
	}
}

求组合数IV——高精度

在这里插入图片描述
在这里插入图片描述
卡特兰数
C 2 n n − C 2 n n − 1 = C 2 n n n + 1 C_{2n}^n-C_{2n}^{n-1}=\frac{C_{2n}^n}{n+1} C2nnC2nn1=n+1C2nn

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int qmi(int a,int k,int p)
{
	int res=1;
	while(k)
	{
		if(k&1)  res=(LL)res*a%p;
		a=(LL)a*a%p;
		k>>=1;
	}
	return res;
}
int main()
{
	int n;
	cin>>n;
	int a=2*n,b=n;
	int res=1;
	for(int i=a;i>a-b;i--)  res=(LL)res*i%mod;
	for(int i=1;i<=b;i++)  res=(LL)res*qmi(i,mod-2,mod)%mod;
	
	res=(LL)res*qmi(n+1,mod-2,mod)%mod;
	
	cout<<res<<endl;
}

吃水果

容斥原理

能被整除的数-二进制

给定一个整数n和m个不同的质数 p i p_i pi, p 2 p_2 p2 p m p_m pm
请你求出1-n中能被 p i p_i pi, p 2 p_2 p2 p m p_m pm中的至少一个整数整除的整数有多少个
输入
10 2
2 3
输出
7

输入
10 3
7 2 5
输出
7

//通过二进制枚举 
#include<iostream>
#include<cstring>
using namespace std;
const int N=20;
int p[N];
typedef long long LL; 
int main()
{
	  int n,m;
	  cin>>n>>m;
	  for(int i=0;i<m;i++) cin>>p[i];
	  
	  int res=0;
	  for(int i=1;i<1<<m;i++) // 2^m
	  {
	     int t=1,cnt=0;   //t:当前所有质数的乘积,cnt:包含几个1
		 for(int j=0;j<n;j++)
		 if(i>>j&1)
		 {
		 	cnt++;
		 	if((LL)t*p[j]>n)
		 	{
		 		t=-1;
		 		break;		
			 }
			 t*=p[j];
		 }	
		 if(t!=-1)
		 {
		 	if(cnt%2)  res+=n/t;   //偶数个
		 	else res-=n/t;
		 }
	  } 
	  cout<<res<<endl;
 } 

博弈论

Nim游戏

给定n堆石子,两位玩家轮流操作,每次操作可以任意从一堆石子中拿走任意数量的狮子(可以拿完,但不能不拿),最后无法进行操作的人视为失败
第一行输入一个整数n,第二行包含n个数字,其中第i个数字表示第i堆石子的数量
如果先手方必胜,则输出“Yes"
否则输出"No"
输入
2
2 3
输出
Yes

分析
a 1 ⊕ a 2 ⊕ a 3 . . . ⊕ a n a_1\oplus a_2\oplus a_3...\oplus a_n a1a2a3...an=0先手必败
a 1 ⊕ a 2 ⊕ a 3 . . . ⊕ a n a_1\oplus a_2\oplus a_3...\oplus a_n a1a2a3...an!=0先手必胜

#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
	int n;
	cin>>n;
	int res=0;
	while(n--)
	{
		int x;
		cin>>x;
		res=res^x;
	}
	if(res) puts("Yes");
	else puts("No");
	return 0;
}

台阶-nim

集合——Nim游戏-sg函数

SG-函数
SG(终点)=0;
SG(x)=(有向图)不能到的最小自然数

给定n堆石子,以及一个由k个不同正整数构成的数字集合S
现在两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合S
最后无法操作的人视为失败
问如果两人都采用最优策略,先手是否获胜
输入
2 k,表示数字集合S中数字的个数
2 5 包含集合s中的k个整数
3 包含整数n
2 4 7 每堆石子的数量
输出
Yes

#include<cstring>
#include<algorithm>
#include<iostream>
#include<unordered_set>
using namespace std;
const int N=110,M=10010;
int n,m;
int s[N],f[M];
int sg(int x)
{
	if(f[x]!=-1)  return f[x];
	unordered_set<int> S;
	for(int i=0;i<m;i++)
	{
		int sum=s[i];
		if(x>=sum)  S.insert(sg(x-sum));
	}
	for(int i=0;;i++)
	  if(!S.count(i))
	    return f[x]=i;
}
int main()
{
	cin>>m;
	for(int i=0;i<m;i++)  cin>>s[i];
	cin>>n;
	memset(f,-1,sizeof f);
	int res=0;
	for(int i=0;i<n;i++)
	{
		int x;
		cin>>x;
		res^=sg(x);
	 } 
	if(res)  puts("Yes");
	else puts("No");
}

取石子游戏

异或数列

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值