蓝桥
等差数列——欧几里得算法
数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一部分的数列,只记得其中 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
ai∗xi=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=a2x1∗a2x2∗a2x3...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=Ca−1b+Ca−1b−1
输入
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!(a−b)!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=Camodpbmodp∗Ca/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}
C2nn−C2nn−1=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
a1⊕a2⊕a3...⊕an=0先手必败
a
1
⊕
a
2
⊕
a
3
.
.
.
⊕
a
n
a_1\oplus a_2\oplus a_3...\oplus a_n
a1⊕a2⊕a3...⊕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");
}