ACwing网址链接:活动 - AcWing
(文章中涉及的一些内容来自ACwing的算法提高课)
1.筛质数
(1)哥德巴赫猜想
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int prime[N],cnt;
bool st[N];
//st[i]==1为合数
//st[i]==0为素数
void init()
{
st[1] = 1;
for (int i = 2; i <= N; i++)
{
if (!st[i])prime[cnt++] = i;
for (int j = 0; prime[j] * i <= N; j++)
{
st[prime[j] * i] = 1;
if (i % prime[j] == 0)break;
}
}
}
int main()
{
init();
int n;
while (cin >> n && n)
{
int i = 1;
while (i)
{
if (!st[n - prime[i]])
{
cout << n << " = " << prime[i] << " + " << n - prime[i] << endl;
break;
}
i++;
}
}
return 0;
}
(2)夏洛克和他的女朋友
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int prime[N], cnt;
bool st[N];
void init(int n)
{
st[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!st[i])prime[cnt++] = i;
for (int j = 0; prime[j] * i <= n; j++)
{
st[prime[j] * i] = 1;
if (i % prime[j] == 0)break;
}
}
}
int main()
{
int n;
cin >> n;
init(n+1);//线性筛求素数,注意价值的范围,至少筛 n + 1 个
if (n > 2)
cout << 2 << endl;
else
cout << 1 << endl;
for (int i = 2; i <= n + 1; i++)
{
if (!st[i])cout << 1 << " ";
else cout << 2 << " ";
}
cout << endl;
return 0;
}
(3)筛质数
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6 + 10;
int prime[N], cnt;
bool st[N];
//n是合数,则一定存在一个质数<=(根号n)
void init(int n)
{
memset(st, 0, sizeof(st));
cnt = 0;
st[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!st[i])prime[cnt++] = i;
for (int j = 0; prime[j] * i <= n; j++)
{
st[prime[j] * i] = 1;
if (i % prime[j] == 0)break;
}
}
}
int main()
{
int l, r;
while (cin >> l >> r)
{
init(5e4);
//prime数组已存好
memset(st, 0, sizeof(st));
//把[l,r]区间内所有的合数用他们的最小质因子筛掉
for (int i = 0; i < cnt; i++)
{
ll p = prime[i];
for (ll j = max(2 * p, (l + p - 1) / p * p); j <= r; j += p)
{
st[j - l] = 1;
}
}
//剩下的所有的都是素数
cnt = 0;
for (int i = 0; i <= r - l; i++)
{
if (!st[i] && i + l > 1)
prime[cnt++] = i + l;
}
if(cnt<2)
printf("There are no adjacent primes.\n");
else
{
int minp = 0, maxp = 0;
for (int i = 0; i + 1 < cnt; i++)
{
int d = prime[i + 1] - prime[i];
if (d < prime[minp + 1] - prime[minp])minp = i;
if (d > prime[maxp + 1] - prime[maxp])maxp = i;
}
printf("%d,%d are closest, %d,%d are most distant.\n",
prime[minp], prime[minp + 1],
prime[maxp], prime[maxp + 1]);
}
}
return 0;
}
2.分解质因数(阶乘分解)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e7 + 10;
//ps:1~n有多少个q的倍数答案为:n/q
//若素数此时为2,则ans/2为1~ans中是2的倍数的个数
//若含有2^2=4,则还要算一次2,故j*=i,最后到j<=n
int prime[N];
int cnt;
bool st[N];
void init(int n)
{
st[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!st[i])prime[cnt++] = i;
for (int j = 0; prime[j] * i <= n; j++)
{
st[prime[j] * i] = 1;
if (i % prime[j] == 0)break;
}
}
}
int main()
{
int n;
cin >> n;
init(n);
ll ans = 0;
for (int i = 2; i <= n; i++)
{
if (st[i])continue;
ans = 0;
for (ll j = i; j <= n; j *= i)//注意longlong!
{
ans += n / j;
}
if(ans)cout << i << " " << ans << endl;
}
return 0;
}
3.快速幂
(1)数列的第k个数
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 200907;
ll qmi(ll a, ll k)
{
ll res = 1;
while (k)
{
if (k & 1)res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
int main()
{
int t;
cin >> t;
while (t--)
{
ll a, b, c;
cin >> a >> b >> c;
ll k;
cin >> k;
if (b - a == c - b)
{
ll d = b - a;
cout << (a + (k - 1) * d % mod) % mod << endl;
}
else
{
ll d = b / a;
cout << a * qmi(d, k - 1) % mod << endl;
}
}
return 0;
}
(2)越狱(输出不能为负数取模)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 100003;
ll qmi(ll a, ll k)
{
ll res = 1;
while (k)
{
if (k & 1)res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
int main()
{
ll m, n;
cin >> m >> n;
//为了避免最终结果res为负数
//(res%mod+mod)%mod----->输出
cout << (qmi(m, n) % mod - m* qmi(m - 1, n - 1) %mod + mod)%mod << endl;
return 0;
}
4.约数个数
(1)轻拍牛头
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 10;
const int M = 1e6 + 10;
int a[N];
int cnt[M];
int res[M];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
cnt[a[i]]++;
}
//枚举可能出现的ai(从小到大!)
for (int i = 1; i <= M; i++)
{
if (!cnt[i])continue;
for (int j = i; j <= M; j += i)
{
res[j] += cnt[i];
}
}
for (int i = 1; i <= n; i++)
cout << res[a[i]] - 1 << endl;
return 0;
}
(2)樱花(推公式约数之和)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
//求(n!)的平方的正约数个数和
//求n!的正约数个数和*2
int n;
int prime[N], cnt;
int c[N];//c[i]为指数
int s[N];//s[i]表示i的最小质因子在prime数组中的位置
bool st[N];
void get_prime()
{
st[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!st[i])
{
prime[++cnt] = i;
s[i] = cnt;
}
for (int j = 1; prime[j] * i <= n; j++)
{
st[prime[j] * i] = 1;
s[prime[j] * i] = j;
if (i % prime[j]==0)break;
}
}
}
void divide(int x)
{
while (x != 1)
{
c[s[x]]++;
x /= prime[s[x]];
//x分解
}
}
int main()
{
cin >> n;
//先筛质数
get_prime();
//由于求阶乘,将1~n分解质因数
for (int i = 1; i <= n; i++)
{
divide(i);
}
int ans = 1;
for (int i = 1; i <= N; i++)
{
if (c[i])
ans = (ll)ans * (2 * c[i] + 1) % mod;
}
cout << ans << endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
//求(n!)的平方的正约数个数和
//求n!的正约数个数和*2
int n;
int prime[N], cnt;
bool st[N];
void get_prime()
{
st[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!st[i])
{
prime[++cnt] = i;
}
for (int j = 1; prime[j] * i <= n; j++)
{
st[prime[j] * i] = 1;
if (i % prime[j]==0)break;
}
}
}
int main()
{
cin >> n;
//先筛质数
get_prime();
int ans = 1;
for (int i = 2; i <= n; i++)
{
if (st[i])continue;
ll res = 0;
for (ll j = i; j <= n; j *= i)res += n / j;
ans = (ll)ans*(res * 2 + 1) % mod;
}
cout << ans << endl;
return 0;
}
5.欧拉函数
(1)可见的点(互质对)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e3 + 10;
int prime[N],cnt;
bool st[N];
int phi[N];
//若要统计一个数N在1~N中与N互质的个数可以用欧拉函数
//可见点的数量等于1到N中每个点i在1~i与i互质的个数之和
//因为是对称的,只需要计算下半部分,斜率y/x小于1,即x要大于y即找与x互质的y一定要比x小
void init()//N<=1000,先进行统计,phi[i]为i与1到N中互质的个数
{
phi[1] = 1;
st[1] = 1;//不是质数
for (int i = 2; i <= N; i++)
{
if (!st[i])
{
prime[cnt++] = i;
phi[i] = i - 1;
//phi[i]=i*(1-1/i)
}
for (int j = 0; prime[j] * i <= N; j++)
{
st[prime[j] * i] = 1;
if (i % prime[j] == 0)
{
phi[prime[j] * i] = phi[i] * prime[j];
break;
}
else
{
phi[prime[j] * i] = phi[i] * (prime[j] - 1);
}
}
}
}
int main()
{
int t;
cin >> t;
init();
for (int i = 1; i <= t; i++)
{
int n;
cin >> n;
int res = 0;//1,1点
for (int j = 1; j <= n; j++)
{
res += phi[j];
}
res *= 2;
res += 1;//1,1点
cout <<i<<" "<<n<<" "<< res << endl;
}
return 0;
}
(2)最大公约数是素数的对数
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e7 + 10;
int prime[N],cnt;
bool st[N];
int phi[N];
ll s[N];
//求gcd(x.y)为素数的(x,y)对数
//gcd(x,y)=p(于0~N) --->
//gcd(x/p,y/p)=1(于0~N/p) --->
//求x1,y1(于0~N/p)互为质数的对数
//思路:先求出1~n中的素数prime,并求欧拉phi(线性筛)
// 由于是n/p故最后的总和为s[n/p]而不是全部phi相加的s[n]
// 由于n/p是随p变的,所以我们需要用前缀和记录
//接着枚举每一个素数p,res+=s[n/p]*2+1;对于每个p,有每组x1和y1,而每组x1和y1于0~n/p也有一定数量的互质对
//乘2是因为欧拉函数只统计斜率为1的下半部分,设斜率为y/x,即x>y
//而y>x也可以,且关于斜率为1对称,加一的(1,1)点
void init(int n)
{
phi[1] = 0;
st[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!st[i])
{
prime[cnt++] = i;
phi[i] = i - 1;
}
for (int j = 0; prime[j] * i <= n; j++)
{
st[prime[j] * i] = 1;
if (i % prime[j] == 0)
{
phi[prime[j] * i] = phi[i] * prime[j];
break;
}
else
{
phi[prime[j] * i] = phi[i] * (prime[j] - 1);
}
}
}
for (int i = 1; i <= n; i++)
{
s[i] = s[i - 1] + phi[i];
}
}
int main()
{
int n;
cin >> n;
init(n);
ll res = 0;
for (int i = 0; i < cnt; i++)
{
int p = prime[i];
res += 2 * s[n/p]+1;
//注意到若res += 2 * s[i],且phi[1]=1,那么(1,1)这个点会被算2次
//所以res+=(2*s[i]+1),且phi[1]=0,这样就不会重复了
}
cout << res << endl;
return 0;
}
6.矩阵乘法
(1)斐波那契前 n 项和
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 3;
int n, m;
//求fn的前n项和Sn%m(其中fn为斐波那契数列)
//F[n]=[f[n],f[n+1]],F[n+1]=[f[n+1],f[n+2]]
//[f[n],f[n+1]][0 1]=[f[n+1],f[n+2]]
// [1 1](设为A)
// F[n]=F[b]*(A^n)
//[f[n],f[n+1],s[n]][0 1 0]=[f[n+1],f[n+2],s[n+1]]
// [1 1 1]
// [0 0 1](设为A)
//F[n]*A=F[n+1],F[n]=F[1]*(A^(n-1))
//答案即为F[2]项
void mul(int c[], int a[], int b[][N])
{
int temp[N] = { 0 };
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
temp[i] =( temp[i] +(ll)a[j] * b[j][i])%m;
}
}
memcpy(c, temp, sizeof(temp));
}
void mul(int c[][N], int a[][N], int b[][N])
{
int temp[N][N] = { 0 };
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
for (int k = 0; k < N; k++)
{
temp[i][j] = (temp[i][j] + (ll)a[i][k] * b[k][j]) % m;
}
}
}
memcpy(c, temp, sizeof(temp));
}
int main()
{
cin >> n >> m;
int a[N][N] = {
{0,1,0},
{1,1,1},
{0,0,1},
};
int f1[N] = { 1,1,1 };
//f1[3]={f0,f1,s0}
n--;
//矩阵快速幂(a^k---->a^n)
while (n)
{
if (n & 1)
mul(f1, f1, a);//res=res*a;
mul(a, a, a);//a=a*a;
n >>= 1;
}
cout << f1[2] << endl;
return 0;
}
(2)佳佳的斐波那契
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 4;
ll n, m;
ll a[N][N] = {
{0,1,1,0},
{1,1,1,0},
{0,0,1,1},
{0,0,0,1},
};
ll X[N] = { 0,1,1,0 };
//X[4]={f0,f1,s1,g0}
void mul(ll c[], ll a[], ll b[][N])
{
ll temp[N] = { 0 };
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
temp[i] =( temp[i] +(ll)a[j] * b[j][i])%m;
}
}
memcpy(c, temp, sizeof(temp));
}
void mul(ll c[][N], ll a[][N], ll b[][N])
{
ll temp[N][N] = { 0 };
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
for (int k = 0; k < N; k++)
{
temp[i][j] = (temp[i][j] + (ll)a[i][k] * b[k][j]) % m;
}
}
}
memcpy(c, temp, sizeof(temp));
}
void qmi(int n)
{
while (n)
{
if (n & 1)mul(X,X, a);//X=X*a;
mul(a,a, a);//a=a*a;
n >>= 1;
}
}
int main()
{
cin >> n >> m;
//Xn=X1*(A^(n-1))=Xn-1*A
//Tn=n*Sn-(Gn-1)
qmi(n - 1);
printf("%lld\n", ((n * X[2] - X[3]) % m + m) % m);
//防止负数输出
return 0;
}
7.组合计数
(1)牡牛和牝牛
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 10;
const int mod = 5000011;
int f[N];
//f[i]表示i头牛排成一排的方案数
//(1)1<=i<=k+1,f[i]=i+1;最多只能放一头D,若放一头则有i个位置可以选或者一头也不放
//(2)i>k+1,考虑最后一头是不是D,若不是则前面i-1头可任意排则方案为f[i-1]
//若是,则前面第i-k到i-1都不能是D,方案为f[i-k-1]
//即f[i]=f[i-1]+f[i-k-1]
int main()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= k + 1; i++)
f[i] = i + 1;
for (int i = k + 2; i <= n; i++)
{
f[i] = (f[i - 1] + f[i - k - 1])%mod;
}
cout << f[n] << endl;
return 0;
}
(2)方程的解
#include<bits/stdc++.h>
using namespace std;
#define ll long long
//a1+a2+...+ak=x^x%1000
//已知k和x,求有多少组a使其成立
const int N = 150, mod = 1000;
int k, x;
//n==x^x
int f[1000][100][N];
//f[i][j]==C[i][j]-->C[n-1][k-1]答案
int qmi(int a, int k)
{
int res = 1;
while (k)
{
if (k & 1)res = (ll)res * a % mod;
a = (ll)a * a % mod;
k >>= 1;
}
return res;
}
//高精度a=b+c
void add(int a[N], int b[N], int c[N])
{
for (int i = 0, t = 0; i < N; i++)
{
t += b[i] + c[i];
a[i] = t % 10;
t /= 10;
}
}
int main()
{
cin >> k >> x;
//n=x^x;
int n = qmi(x % mod, x);
//C(n-1,k-1)
//C(i,j)=C(i-1,j-1)+C(i-1,j)
for (int i = 0; i < n; i++)
{
for (int j = 0; j <= i && j < k; j++)
{
if (j == 0)f[i][j][0] = 1;
else add(f[i][j], f[i - 1][j], f[i - 1][j - 1]);
}
}
//输出答案
int tt = N - 1;
while (!f[n - 1][k - 1][tt])tt--;
while (tt >= 0)
cout << f[n - 1][k - 1][tt--];
return 0;
}
(3)车的放置
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 100003;
const int N = 2010;
//(N开两倍是因为出现了a+c)
// C(a,b)=a!/(b!*(a-b)!)=f[a]*inf[b]*inf[a-b]
// A(a,b)=a!/(a-b)!=f[a]*inf[a-b]
//答案C(b,i)*A(a,i)*C(d,k-i)*A(a+c-i,k-i)
int a, b, c, d, k;
int f[N], inf[N];
int qmi(int a, int k)
{
int res = 1;
while (k)
{
if (k & 1)res =(ll)res * a % mod;
k >>= 1;
a = (ll)a * a % mod;
}
return res;
}
int C(int a, int b)
{
if (a < b)return 0;
return (ll)f[a] * inf[b] % mod * inf[a - b] % mod;
}
int A(int a, int b)
{
if (a < b)return 0;
return (ll)f[a] * inf[a - b] % mod;
}
int main()
{
cin >> a >> b >> c >> d >> k;
f[0] = inf[0] = 1;
for (int i = 1; i < N; i++)
{
f[i] = (ll)f[i - 1] * i % mod;
inf[i] = (ll)inf[i - 1] * qmi(i, mod - 2) % mod;//求逆元!
}
int res = 0;
for (int i = 0; i <= b; i++)
{
res = (res + (ll)C(b, i) * A(a, i) % mod * C(d, k - i) % mod * A(a + c - i, k - i) % mod)%mod;
}
//防止输出负数
cout << (res % mod + mod) % mod << endl;
return 0;
}
(4)数字三角形
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1000 + 10;
//给n*m的网格,计算三角形个数
int n, m;
ll ans = 0;
//总数C(n*m,3)-不合法
//(1)水平n*C(m,3)---->n,m格点数
//(2)竖直m*C(n,3)---->n,m格点数
// ---->n,m为边长
//(3)斜线(斜率大于0):底为j,高为i的直角三角形,共有(n-i+1)*(m-j+1)种三角形
//其中一个三角形的斜边对三点贡献即为(gcd(i,j)-1)种
//共有(包括斜率小于0)2*(n-i+1)*(m-j+1)*(gcd(i,j)-1)种
//答案为C(n*m,3)-2*(n-i+1)*(m-j+1)*(gcd(i,j)-1)
int gcd(int a, int b)
{
return b ? gcd(b,a%b):a;
}
ll C(ll a, ll b)
{
return a * (a - 1) * (a - 2) / 6;
}
int main()
{
cin >> n >> m;
//转换为格点数
n++, m++;
ll res = C(n * m, 3) - n * C(m, 3) - m * C(n, 3);
//转换成长度
n--, m--;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
res -= 2 * (n - i + 1) * (m - j + 1) * (gcd(i, j) - 1);
}
}
cout << res << endl;
return 0;
}
(5)序列统计
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e6 + 3;
//序列统计:统计长度在1~N之间,元素大小都在L到R之间的
//单调不降序列的数量。设序列长度为k
//[l,r]---->[0,r-l]映射
//0<=a1<=a2<=...<=ak<=r-l,其中ai是[0,r-l]的序列个数
//令x1=a1,x2=a2-a1,..,xk=ak-(ak-1),即0<=x1+x2+..+xk<=r-l
//求满足条件的{xk}数列的个数-->用不超过r-l的小球放入k个盒子,盒子可为空的方案数
int qmi(int a, int k)
{
int res = 1;
while (k)
{
if (k & 1)res = (ll)res * a % mod;
a = (ll)a * a % mod;
k >>= 1;
}
return res;
}
int C(int a, int b)
{
int res = 1, inv = 1;
for (int i = 1, j = a; i <= b; i++, j--)
{
res = (ll)res * j % mod;
inv = (ll)inv * i % mod;
}
res = (ll)res * qmi(inv, mod - 2) % mod;
return res;
}
int lucas(ll a, ll b)
{
if (a < mod && b < mod)return C(a, b);
return (ll)C(a%mod,b%mod)*lucas(a/mod,b/mod)%mod;
}
int main()
{
int t;
cin >> t;
while (t--)
{
ll n, l, r;
cin >> n >> l >> r;
int res = lucas(r - l + n + 1, r - l + 1) - 1;
cout << (res % mod + mod) % mod << endl;
}
return 0;
}