文章目录
本文持续更新。
求因子个数
long long f(long long n) //计算正整数的因子的个数
{
long long cnt = 0;
for(long long i = 1; i * i <= n; i++)
if(n % i == 0)
if(i * i != n) cnt += 2;
else cnt++;
return cnt;
}
判断素数
#define maxn 1000000 + 10
int vis[maxn];
void isprime()
{
memset(vis, 0, sizeof(vis));
vis[1] = 1;
for(long long i = 2; i * i <= maxn; i++)//如果一个合数>sqrt(maxn),那么它必定在前面已经被标记过。
if(!vis[i])
for(long long j = i * i; j <= maxn; j += i)//只需从i*i开始判断即可。
vis[j] = 1;
}
/*
vis[i]=1表示不是素数,vis[i]=0表示是素数
i * i的数据范围可能会超过int
*/
#define maxn 1000000 + 10
int vis[maxn];
void isprime()
{
memset(vis, 0, sizeof(vis));
vis[1] = 1;
for(long long i = 2; i * i < maxn; i++)//如果一个合数>sqrt(maxn),那么它必定在前面已经被标记过。
if(!vis[i])
for(long long j = i * i; j < maxn; j += i)//只需从i*i开始判断即可。
vis[j] = 1;
}
/*
vis[i]=1表示不是素数,vis[i]=0表示是素数
i * i的数据范围可能会超过int
*/
bool Is_Prime(ll n)
{
for(int i = 2; i * i <= n; i++)
if(n % i == 0) return false;
return n != 1;
}
找 n 的最大质因数在素数表的位置,规定1在素数表的位置为0 。
#include<bits/stdc++.h>
using namespace std;
int prime[1000010] = {0};
int mark[1000010] = {0};
void Init()
{
int m = 1;
for(int i = 2; i < 1000010; i++)
{
if(prime[i] == 0)
{
mark[i] = m++; //记录素数所在素数表的位置
for(int j = i; j < 1000010; j += i)
prime[j] = i;
}
}
}
int main()
{
int n;
Init();
while(~scanf("%d", &n))
printf("%d\n", mark[prime[n]]);
return 0;
}
/*
筛法的思想,将某个素数的所有倍数置为这个素数本身,由于题目要求的是最大质因数,所以从小到大遍历。
举个例子,如果一个数n的质因数有2,7,9,那么遍历到2时,这个数被标记为2,用标记数组mark进行标记。
遍历到7时,由于prime[7]==0,这个数被标记为7,(2就被覆盖掉了)。
继续往下遍历,遍历到9时,由于prime[9]==0,这个数被标记为9,(7被覆盖),一直遍历到最后也没找到下一个质因数了,那么就输出9。
*/
快速幂取模
ll Quick_Mod(ll a, ll b, ll mod)
{
ll res = 1, term = a % mod;
while(b)
{
if(b & 1) res = (res * term) % mod;
term = (term * term) % mod;
b >>= 1;
}
return res;
}
ll Quick_Mul(ll a, ll b)
{
ll res = 1, m = b;
while(m)
{
if(m & 1) res =(res * a) % b;
a = (a * a) % b;
m >>= 1;
}
return res;
}
最大公因数(gcd)、最小公倍数(lcm)
long long gcd(long long a, long long b)
{
return !b ? a : gcd(b, a % b);
}
long long lcm(long long a, long long b)
{
return a / gcd(a, b) * b;
}
回文数、回文串
void Is_Number_Palindrome(int num) //判断一个数是否为回文数
{
int y = 0, s = num;
while(s > 0)
{
y = y * 10 + s % 10;
s = s / 10;
}
y == num ? printf("%d回文\n", num) : printf("%d不回文\n", num);
}
void Is_String_Palindrome(char a[]) //判断一个字符串是否为回文串
{
int j, i, n;
char b[1000];
n = strlen(a);
for(i = 0, j = n - 1; i < n; i++, j--)
b[j] = a[i];
for(i = 0; i < n; i++)
if(b[i] != a[i]) break;
i == n ? printf("回文\n") : printf("不回文\n");
}
进制转换
#include<bits/stdc++.h>
using namespace std;
char num[100];
char s[36] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
int n, m, step;
void solve(int a, int b) //十进制数 a 转化为 b 进制数并输出
{
step = 0;
if(a < 0)
{
cout << "-";
a = -a;
}
while(a)
{
num[step++] = s[a % b];
a /= b;
}
for(step -= 1; step >= 0; step--)
cout << num[step];
cout << endl;
}
int main ()
{
while(cin >> n >> m) //十进制数n转化为m进制数
solve(n, m);
return 0;
}
高精度运算
最长公共子序列(LCS)
求最长的公共子序列,如果有多个,随便输出一个就行。
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000 + 10
//状态转移方程:
//if(s[i] == t[j]) dp[i + 1][j + 1] = dp[i][j] + 1;
//else dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]);
int dp[maxn][maxn];
char a[maxn], b[maxn];
int vis[maxn][maxn];//只求长度的时候可以去掉
//如果要输出公共子序列,把int改成void,并把return去掉
int len1, len2;
//int LCS()
void LCS()
{
// int len1, len2, i, j;
int i, j;
len1 = strlen(a);
len2 = strlen(b);
memset(dp, 0, sizeof(dp));
for(i = 1; i <= len1; i++)
for(j = 1; j <= len2; j++)
{
if(a[i - 1] == b[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else if(dp[i - 1][j] >= dp[i][j - 1])
{
dp[i][j] = dp[i - 1][j];
vis[i][j] = 1;
}
else
{
dp[i][j] = dp[i][j - 1];
vis[i][j] = -1;
}
}
// return dp[len1][len2];
}
// 这个函数可以将LCS打印出来
void Print(int i, int j) //i,j分别代表a,b字符串的长度
{
if(i == 0 || j == 0) return;
if(!vis[i][j]) //如果a[i]和b[j]子母相同,输出
{
Print(i - 1, j - 1);
printf("%c", a[i - 1]);
}
else if(vis[i][j] == 1) Print(i - 1, j); //如果dp[i - 1][j] > dp[i][j - 1]
else Print(i, j - 1); //如果dp[i][j - 1] >= dp[i - 1][j]
}
int main()
{
while(cin >> a >> b)
{
LCS();
Print(len1, len2);
}
return 0;
}
最长上升子序列(LIS)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50005;
int num[maxn], dp[maxn], n;
void LIS()
{
dp[0] = num[0];
int pos, L = 1;
for(int i = 0; i < n; i++)
{
pos = lower_bound(dp, dp + L, num[i]) - dp;
dp[pos] = num[i];
L = max(L, pos + 1);
}
cout << L << endl;
}
int main()
{
while(cin >> n)
{
for(int i = 0; i < n; i++)
cin >> num[i];
LIS();
}
return 0;
}
归并排序
昨天看了鱼腥草的视频,就偷学到一个模板,开心
例题:求逆序数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
int q[N], tmp[N];
ll merge_sort(int l, int r)
{
if(l >= r) return 0;
int mid = l + r >> 1;
ll res = merge_sort(l, mid) + merge_sort(mid + 1, r);
//归并的过程
int k = 0, i = l, j = mid + 1;
while(i <= mid && j <= r)
if(q[i] <= q[j]) tmp[k++] = q[i++];
else
{
tmp[k++] = q[j++];
res += mid - i + 1;
}
//扫尾
while(i <= mid)
tmp[k++] = q[i++];
while(j <= r)
tmp[k++] = q[j++];
//物归原主
for(int i = l, j = 0; i <= r; i++, j++)
q[i] = tmp[j];
return res;
}
int main()
{
int n;
while(cin >> n)
{
memset(q, 0, sizeof(q));
memset(tmp, 0, sizeof(tmp));
for(int i = 0; i < n; i++)
cin >> q[i];
cout << merge_sort(0, n - 1) << endl;
}
return 0;
}
快速排序
#include<bits/stdc++.h>
using namespace std;
int num[100005];
void quick_sort(int q[], int l, int r)
{
if(l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r + 1 >> 1]; //注意是向上取整,因为向下取整可能使得x取到q[l]
while(i < j)
{
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i], q[j]);
}
quick_sort(q, l, i - 1);
quick_sort(q, i, r);
}
int main()
{
int n;
while(cin >> n)
{
memset(num, 0, sizeof(num));
for(int i = 0; i < n; i++)
cin >> num[i];
quick_sort(num, 0, n - 1);
for(int i = 0; i < n; i++)
i == 0 ? cout << num[i] : cout << " " << num[i];
cout << endl;
}
return 0;
}
Bash博弈
#include<bits/stdc++.h>
using namespace std;
//有一堆石子共有 N 个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗,拿到最后 1 颗石子的人获胜。
void Bash_game(int N, int K)
{
if(N % (K + 1) == 0) cout << "B" << endl;
else cout << "A" << endl;
// if(N % (K + 1) == 0) cout << "Last one take win" << endl;
// else cout << "First one take win" << endl;
}
int main()
{
int T, n, k;
while(cin >> T)
{
while(T--)
{
cin >> n >> k;
Bash_game(n, k);
}
}
return 0;
}
Nim博弈
#include<bits/stdc++.h>
using namespace std;
#define N 1000 + 10
//有 N 堆石子。A B两个人轮流拿,A先拿。每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后 1 颗石子的人获胜。
int a[N]; //有N堆石子
void Nim_game()
{
int res = a[0];
for(int i = 1; i <= N; i++)
res ^= a[i];
if(res) printf("A\n");
else printf("B\n");
// if(res) printf("Firse one take win\n");
// else printf("Last one take win\n");
}
int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
Nim_game();
}
return 0;
}
威佐夫博奕
#include<bits/stdc++.h>
using namespace std;
const double q = (1 + sqrt(5.0)) / 2.0;
//有两堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从两堆中取相同数量的石子,但不可不取。拿到最后一颗石子的人获胜。
bool Witzov_game(int a, int b) //a代表第一堆,b代表第二堆
{
if(a > b) swap(a, b);
int k = b - a;
if(a == (int)(k * q)) return 0;
return 1;
}
int main()
{
int T, a, b;
while(cin >> T)
{
while(T--)
{
cin >> a >> b;
if(Witzov_game(a, b) == 1) cout << "A\n";
else cout << "B\n";
}
}
return 0;
}
中国剩余定理
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll prime[105], r[105];
ll extended_euclid(ll a, ll b, ll &x, ll &y)
{
ll d;
if(b == 0)
{
x = 1;
y = 0;
return a;
}
d = extended_euclid(b, a % b, y, x);
y -= a / b * x;
return d;
}
ll chinese_remainder(ll b[], ll w[], ll len)
{
ll i, d, x, y, m, n, ret;
ret = 0; n = 1;
for(i = 0; i < len ;i++)
n *= w[i];
for(i = 0; i < len ;i++)
{
m = n / w[i];
d = extended_euclid(w[i], m, x, y);
ret = (ret + y * m * b[i]) % n;
}
return (n + ret % n) % n;
}
int main()
{
int n;
while(cin >> n)
{
for(int i = 0; i < n; i++)
cin >> prime[i] >> r[i];
cout << chinese_remainder(r, prime, n) << endl;
}
return 0;
}
约瑟夫环
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, k;
while(cin >> n >> k)
{
int res = 0;
for(int i = 2; i <= n; i++)
res = (res + k) % i;
cout << res + 1 << endl;
}
return 0;
}
最长回文子串长度(Manacher算法)
#include<bits/stdc++.h>
using namespace std;
char ch[1000005];
int vis[1000005];
void Manacher()
{
int l = strlen(ch);
//处理字符串,在字符串开头,结尾都加上'#'
for(int i = l; i > 0; i--)//注意是从最后一位开始处理
{
ch[2 * i] = ch[i - 1];
ch[2 * i + 1] = '#';
}
ch[0] = '$';//避免出现越界问题
ch[1] = '#';
int id, mx, ans;//id最大回文子串中心的位置,mx最大回文子串的边界
id = mx = ans = 0;
for(int i = 1; i <= 2 * l + 1; i++)
{
if(mx > i) vis[i] = min(vis[2 * id - i], mx - i);
else vis[i] = 1;
while(ch[i + vis[i]] == ch[i - vis[i]]) vis[i]++;
if(mx < vis[i] + i)
{
id = i;
mx = vis[i] + i;
}
ans = max(ans, vis[i]);
}
printf("%d\n", ans - 1);
}
int main()
{
while(cin >> ch)
{
Manacher();
memset(ch, 0, sizeof(ch));
memset(vis, 0, sizeof(vis));
}
return 0;
}
压位技巧
例题:N的阶乘
输入N求N的阶乘的准确值。
#include<bits/stdc++.h>
using namespace std;
long long mod = 1e9, num[1000005] = {1};//第一个元素值为1
int main()
{
int n;
while(cin >> n)
{
int cnt = 0;
for(int i = 2; i <= n; i++)
{
long long step = 0;
for(int j = 0; j <= cnt; j++)
{
num[j] = num[j] * i + step; //计算阶乘
step = num[j] / mod; //计算进位
num[j] %= mod; //只保存9位数
}
if(step) num[++cnt] = step; //如果有进位,说明还需要继续运算
}
cout << num[cnt]; //防止多输出
for(int i = cnt - 1; i >= 0; i--)
printf("%09lld", num[i]); //不足9位补0输出
printf("\n");
}
return 0;
}
斯特林近似
例题:N的阶乘的长度
输入N求N的阶乘的10进制表示的长度。例如6! = 720,长度为3。
#include<bits/stdc++.h>
using namespace std;
#define PI 3.14159265358
int main()
{
double n;
while(cin >> n)
{
long long res = (log(n * 2 * PI) / 2 + n * log(n) - n) / log(10) + 1;//0.5 * log10(2 * PI * n) + n * log10(n / e) + 1;
cout << res << endl;
}
return 0;
}
前缀和
例题:子段求和,查询第I个元素开始,长度为L的子段和。
#include<bits/stdc++.h>
using namespace std;
long long sum[50005], a[50005];
void Prefix(int n) //前缀和
{
memset(sum, 0, sizeof(sum));
for(int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
}
int main()
{
int n, Q, I, L;
while(cin >> n)
{
memset(a, 0, sizeof(a));
for(int i = 1; i <= n; i++)
cin >> a[i];
cin >> Q;
Prefix(n);
for(int i = 0; i < Q; i++)
{
cin >> I >> L;
cout << sum[I + L - 1] - sum[I - 1] << endl;
}
}
return 0;
}
01背包(每种物品只有一个)
#include<bits/stdc++.h>
using namespace std;
//n 物品数量,v 背包容量
//size 单个物品体积,value 单个物品价值
int n, v, size[10005], value[10005], dp[10005];
void bag01()
{
for(int i = 0; i < n; i++)
for(int j = v; j >= size[i]; j--)
dp[j] = max(dp[j], dp[j - size[i]] + value[i]);
cout << dp[v] << endl;
}
int main()
{
while(cin >> n >> v)
{
memset(size, 0, sizeof(size));
memset(value, 0, sizeof(value));
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; i++)
cin >> size[i] >> value[i];
bag01();
}
return 0;
}
完全背包(每种物品有无穷多)
多重背包(每种物品数量有限)
欧拉函数
#include<bits/stdc++.h>
using namespace std;
//欧拉函数公式:
//φ(N)=N*(1-1/P1)*(1-1/P2)*...*(1-1/Pn).
int Eular(int n)
{
int eu = n;
for(int i = 2 ; i * i <= n ; i++)
{
if(n % i == 0)
{
eu -= eu / i;
while(n % i == 0)
n /= i;
}
}
if(n > 1) eu -= eu / n;
return eu;
}
//欧拉函数打表
/*
void Eular()
{
euler[1] = 1;
for(int i = 2; i < maxn; i++)
euler[i] = i;
for(int i = 2; i < maxn; i++)
if(euler[i] == i)
for(int j = i; j < maxn; j += i)
euler[j] = euler[j] / i * (i - 1);//先进行除法是为了防止中间数据的溢出
}
*/
int main()
{
int n;
while(cin >> n)
cout << Eular(n) << "\n";
return 0;
}
RMQ算法
计算区间内最大的数、最小的数、最大最小数之差。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10005;
int num[MAXN], F_Min[MAXN][20], F_Max[MAXN][20], n, query;
void Init()
{
for(int i = 1; i <= n; i++)
F_Min[i][0] = F_Max[i][0] = num[i];
for(int i = 1; (1 << i) <= n; i++) //按区间长度递增顺序递推
for(int j = 1; j + (1 << i) - 1 <= n; j++) //区间起点
{
F_Max[j][i] = max(F_Max[j][i - 1], F_Max[j + (1 << (i - 1))][i - 1]);
F_Min[j][i] = min(F_Min[j][i - 1], F_Min[j + (1 << (i - 1))][i - 1]);
}
}
int Query_max(int l, int r)
{
int k = (int)(log(double(r - l + 1)) / log((double)2));
return max(F_Max[l][k], F_Max[r - (1 << k) + 1][k]);
}
int Query_min(int l, int r)
{
int k = (int)(log(double(r - l + 1)) / log((double)2));
return min(F_Min[l][k], F_Min[r - (1 << k) + 1][k]);
}
int main()
{
while(cin >> n)
{
memset(num, 0, sizeof(num));
memset(F_Max, 0, sizeof(F_Max));
for(int i = 1; i <= n; i++)
cin >> num[i];
cin >> query;
Init();
int a, b;
for(int i = 0; i < query; i++)
{
cin >> a >> b;
cout << Query_max(a + 1, b + 1) << endl;
// cout << Query_min(a + 1, b + 1) << endl;
// cout << Query_max(a + 1, b + 1) - Query_min(a + 1, b + 1) << endl;
}
}
return 0;
}
快速乘
时间复杂度O(1)
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
ll modmul(ll A, ll B, ll Mod)
{
return (A * B -(ll)((long double)A * B / Mod) * Mod + Mod) % Mod;
}
int main()
{
ll a, b;
while(cin >> a >> b)
cout << modmul(a, b, mod) << "\n";
return 0;
}
二分乘法
用来解决乘法的结果远超long long范围,但需要的结果有取余的乘法运算。
时间复杂度和快速幂一样,为O(logn)
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
ll Quick_Mul(ll a, ll b, ll m)
{
ll ans = 0, k = a, f = 1; //f是用来存负号的
if(k < 0) {f = -1; k = -k;}
if(b < 0) {f *= -1; b = -b;}
while(b)
{
if(b & 1) ans = (ans + k) % m;
k = (k + k) % m;
b >>= 1;
}
return ans * f;
}
int main()
{
ll a, b;
while(cin >> a >> b)
cout << Quick_Mul(a, b, mod) << "\n";
return 0;
}
乘法逆元、扩展欧几里得
给出2个数M和N(M < N),且M与N互质,找出一个数K满足0 < K < N且K * M % N = 1,如果有多个满足条件的,输出最小的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
ll res = exgcd(b, a % b, y, x);
y -= x * (a / b);
return res;
}
ll inv(ll a, ll n)
{
ll x, y;
exgcd(a, n, x, y);
x = (x % n + n) % n;
return x;
}
int main()
{
int m, n;
while(cin >> m >> n)
cout << inv(m, n) << "\n";
return 0;
}
全排列(DFS实现、STL实现)
#include<bits/stdc++.h>
using namespace std;
int vis[10];//用于标记是否被访问过,0--未访问 1--已访问
int ans[10];//用于存储答案
int step, n;
void dfs(int step)
{
for(int i = 1; i <= n; i++)
{
if(vis[i] == 0)
{
vis[i] = 1;
ans[step] = i;//将答案存储
if(step < n) dfs(step + 1); //调用递归,即相当于再一次从头执行一个dfs函数,可以理解为一个嵌套
else
{
for(int i = 1; i <= n; i++)
{
printf("%d", ans[i]);
if(i != n) printf(" ");
else printf("\n");
}
}
vis[i] = 0; //访问完毕返回标记为可访问 只有当输出了一个结果后才有可能执行
}
}
}
int main()
{
memset(vis, 0, sizeof(vis));
step = 0;
scanf("%d", &n);
dfs(1);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int ans[10];
void Full(int n)
{
for(int i = 0; i < n; i++)
ans[i] = i + 1;
do
{
for(int i = 0; i < n; i++)
{
if(i != 0) cout << " ";
cout << ans[i];
}
cout << "\n";
}
while(next_permutation(ans, ans + n));
}
int main()
{
int n;
memset(ans, 0, sizeof(ans));
scanf("%d", &n);
Full(n);
return 0;
}
整数二分、实数二分
整数二分:
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while(l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while(l < r)
{
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
实数二分:
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while(r - l > eps)
{
double mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid;
}
return l;
}