GCD & LCM
int gcd(int a,int b)
{
return b ? gcd(b, a % b) : a;
}
int lcm(int a, intb)
{
return abs(a * b) / gcd(a, b);
}
int a, b, x, y;
int exgcd(int a, int b, int &x, int &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
int ans = exgcd(b, a % b, y, x);
y -= x * (a / b);
return ans;
}
字符串 <=> 整型
sscanf(s, "%d", &n);//从字符串s中读数n
sprintf(s, "%d", n);//将n转换为字符串s
同余
(a + b) % m = (a % m) + (b % m);
(a - b) % m = (a % m) - (b % m); // 稍微留意负数情况
(a * b) % m = (a % m) * (b % m);
m ^ n % c = (m % c) ^ n % c;
strtok和sscanf结合输入
int main()
{
char s[] = "ab-cd:ef;gh: i-jkl;mnop;qrs-tu:vwx-y;z";
char *delim = "-: "; //分割条件字符串,目前里面有三个字符‘-’,’:’,‘ ’
char *p; //当目标字符串s中遇到分割条件字符时自动改写成‘\0’
printf("%s ", strtok(s, delim));
while ((p = strtok(NULL, delim)))
printf("%s ", p);
printf("\n");
}
int main()
{
int a, b, c;
char str[] = "2018:7:15";
sscanf(str, "%d:%d:%d", &a, &b, &c);
cout << a << " " << b << " " << c << endl;
}
数组去重
// Eg:1 2 2 3 4 => 1 2 3 4 2
int b[N];
sort(b, b + N);
int len = unique(b, b + n) - b;//返回的是4的位置
求 n ! n! n! 的位数 – 斯特林公式
这里求到的数和原本的实际值相差不大,故求位数不影响
len = 0.5 * log10(2 * 3.1415927 * n) + n * log10(n / 2.718281828459);
ans = (int)len + 1;
Log与自然对数e
Log(x)表示ln(x) ,其他例如:log10(x) ,log2(x),用exp(x)来表示e^x
分数
储存分数的结构体
struct F
{
int son, mother; // 分子,分母
F(int s, int m) : son(s), mother(m) {}
};
化为最简分数
void simplify(F &f)
{
if (f.son == 0)
{ // 分子为0,分母均变为1
f.mother = 1;
return;
}
if (f.mother < 0)
{ // 把分母上的负号移到分子上
f.son = -f.son;
f.mother = -f.mother;
}
int d = gcd(abs(f.son), f.mother); // 求分子分母最大公约数
f.son /= d;
f.mother /= d;
}
输出
void output(Fraction &f)
{
if (f.mother == 1)
printf("%d", f.son);
else
{
if (abs(f.son) < f.mother)
printf("%d/%d", f.son, f.mother);
else
printf("%d %d/%d", f.son / f.mother, abs(f.son) % f.mother, f.mother);
}
}
求1到n的数的异或和 O(1)
unsigned xor_n(unsigned n)
{
unsigned t = n & 3;
if (t & 1)
return t / 2u ^ 1; // 照着打就行,u默认为unsigned int
return t / 2u ^ n;
}
基姆拉尔森公式
卡特兰数
给定一个凸n边形,问将其划分成三角形的方法数
1,1,2,5,14,42,132….
h(n) = h(n - 1) * (4 * n - 2) / (n + 1) h(n) = C(2n, n) / (n + 1);
h(0) = 1;
错排公式
Eg:给定n种颜色篮子和n种颜色球,求全放错情况数
D(1) = 0;
D(2) = 1;
D(n) = (n - 1) * (D(n - 2) + D(n - 1));
进制转换
简单输出查看
#include <bitset>
#include <iostream>
using namespace std;
int main()
{
cout << "35的2进制: " << bitset<8>(35) << endl; //<8>:表示保留8位输出
cout << "35的8进制:" << oct << 35 << endl;
cout << "35的10进制" << dec << 35 << endl;
cout << "35的16进制:" << hex << 35 << endl;
}
其他->十进制
手写
int Atoi(string s, int radix) // s是给定的radix进制字符串
{
int ans = 0;
for (int i = 0; i < s.size(); i++)
{
char t = s[i];
if (t >= '0' && t <= '9')
ans = ans * radix + t - '0';
else
ans = ans * radix + t - 'a' + 10;
}
return ans;
}
strtol
long int strtol(const char *nptr, char **endptr, int base)
将字符串变为long
base是要转化的数的进制,非法字符会赋值给endptr,nptr是要转化的字符
#include<cstdio>
int main()
{
char buffer[20]="10549stend#12";
char *stop;
int ans=strtol(buffer, &stop, 8); //将八进制数1054转成十进制,后面均为非法字符
printf("%d\n",ans);
printf("%s\n", stop);
}
十进制->其他
手写
string intToA(int n, int radix) // n是待转数字,radix是指定的进制
{
string ans = "";
do
{
int t = n % radix;
if (t >= 0 && t <= 9)
ans += t + '0';
else
ans += t - 10 + 'a';
n /= radix;
} while (n != 0); // 使用do{}while()以防止输入为0的情况
reverse(ans.begin(), ans.end());
return ans;
}
itoa() Win特有
char *itoa(int value, char *string, int radix);
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
int num = 100;
char s[25];
itoa(num, s, 100);
cout << s; // 输出100
}
sprintf() 通用
char s[255];
sprintf(s, "%x", 100); //将100转为16进制表示的字符串。
#include<cstdio>
int main()
{
char s[100]={0};
sprintf(s, "%d", 123); //十进制输出产生"123"
sprintf(s, "%4d%4d", 123, 4567); //指定宽度不足的左边补空格,产生:" 1234567"
sprintf(s, "%8o", 123); //八进制输出,宽度占8个位置
sprintf(s, "%8x", 4567); //小写16 进制,宽度占8个位置,右对齐
sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142"
int i = 100;
sprintf(s, "%.2f", i); //注意这是不对的
sprintf(s, "%.2f", (double)i); //要按照这种方式才行
}
全排列
数字
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int a[4] = {1, 2, 3, 4};
sort(a, a + 4);
do
{
for (int i = 0; i < 4; i++)
cout << a[i] << " ";
cout << endl;
} while (next_permutation(a, a + 4));
return 0;
}
字母
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
string s;
cin >> s;
sort(s.begin(), s.end());
do
{
cout << s << endl;
} while (next_permutation(s.begin(), s.end()));
}
第n个排列
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int a[7] = {1, 2, 3, 4, 5, 6, 7};
sort(a, a + 7);
int n = 0;
do
{
if (n == 1654)
{
for (int i = 0; i < 7; i++)
cout << a[i];
cout << endl;
break;
}
n++;
} while (next_permutation(a, a + 7));
}
素数
判定
bool check(long long n)
{
if (n <= 3)
return n > 1;
if (n % 6 != 1 && n % 6 != 5)
return false;
int t = sqrt(n);
for (long long i = 5; i <= t; i += 6)
{
if (n % i == 0 || n % (i + 2) == 0)
return false;
}
return true;
}
生成n个素数
int ans[100005];
bool vis[10000000];
void CreatPrime()
{
int cnt = 1;
for (int i = 2; i < 1300000; i++)
{
if (vis[i])
continue;
for (int j = i + i; j < 10000000; j += i)
vis[j] = true;
if (!vis[i])
ans[cnt++] = i;
if (cnt > 100000)
{
break;
}
}
int PrimeNum;
cin >> PrimeNum;
{
for (int i = 1; i <= PrimeNum; i++)
printf("%d ", ans[i]);
}
}
生成n以为的素数表
vector<int> prime; // 素数表存储在prime中,prime是全局变量
void findPrime(int n)
{
vector<bool> f(n, false);
for (int i = 2; i < n; ++i)
if (!f[i])
{
prime.push_back(i);
for (int j = i + i; j < n; j += i)
f[j] = true;
}
}
将n质因子分解
map<int, int> factor; // 质因子在factor中,键表示质因子,值表示该质因子个数,注意map会按键排序
void getFactor(int n)
{
if (n == 1)
{
factor[1] = 1;
return;
}
findPrime(n + 1); // 打印n+1以内的素数表存储到prime中
for (int i : prime)
{
while (n % i == 0)
{
++factor[i];
n /= i;
}
if (n == 1)
break;
}
}
日期处理
// 给定年月日O(1)算周数
int CaculateWeekDay(int y, int m, int d)
{
if (m == 1 || m == 2)
{
m += 12;
y--;
}
int iWeek = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
return iWeek;
}
// 周日用数字0表示
// 判断y年m月有几天
int monthdays[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 平年时每个月有多少天
int daysOfMonth(int y, int m)
{
if ((y % 400 == 0 || y % 4 == 0 && y % 100 != 0) && m == 2) // y年是闰年且查询2月有几天
return 29; // 闰年2月有29天
return monthdays[m]; // 直接返回monthdays的相应位置的天数
}
// 根据1850年1月1日是周二,返回y年m月d日是周几
int determineWeek(int y, int m, int d, int week = 2)
{
for (int i = 1850; i < y; ++i)
{ // 检查1850年到y年经历的年份
int temp = (i % 400 == 0 || i % 4 == 0 && i % 100 != 0) ? 366 : 365; // 平年有365天,闰年有366天
week = (week + temp) % 7; // 更新week
}
for (int i = 1; i < m; ++i) // 检查1月到m月的月份
week = (week + daysOfMonth(y, i)) % 7; // 求出该月有几天,并更新week
return (week + d - 1) % 7; // 返回周几,注意周日用0表示
}
// 判断y年m月第b个星期c是几号
int determineDayOfNumberWeek(int y, int m, int b, int c)
{
int week = determineWeek(y, m, 1); // 确定y年m月1日是周几
return 1 + (c + 7 - week) % 7 + 7 * (b - 1);
}
快速幂
typedef long long LL
LL pow_mod(LL a, LL n)
{
LL res = 1;
while (n)
{
if (n & 1)
res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
龟速乘
速度较慢,但不会爆long long
long long quick_mul(long long x, long long y, long long mod)
{
long long ans = 0;
while (y != 0)
{
if (y & 1 == 1)
ans += x, ans %= mod;
x = x + x, x %= mod;
y >>= 1;
}
return ans;
}
long long quick_pow(long long x, long long y, long long mod)
{
long long sum = 1;
while (y != 0)
{
if (y & 1 == 1)
sum = quick_mul(sum, x, mod), sum %= mod;
x = quick_mul(x, x, mod), x %= mod;
y = y >> 1;
}
return sum;
}
快速幂里的x是指数级增长,而龟速乘变成了翻倍,仅此而已。
快速乘
本来存进long long会炸掉的值先进行计算,用long double暂时存下,然后再把差值——一个不会超过long long的数字塞回去,再特判一下精度问题
cin >> a >> b >> mod;
cout << ((a * b - (long long)((long double)a * b / mod) * mod + mod) % mod);
矩阵快速幂
int f = 2;
long long n, mod = 1024;
struct node
{
LL materix[3][3];
};
node mul(node a, node b)
{
node res;
memset(res.materix, 0, sizeof(res.materix));
for (int i = 1; i <= f; ++i)
{
for (int j = 1; j <= f; ++j)
{
for (int k = 1; k <= f; ++k)
{
res.materix[i][j] = (res.materix[i][j] + a.materix[i][k] * b.materix[k][j]) % mod;
}
}
}
return res;
}
node ksm(node a, long long b)
{
node ans;
memset(ans.materix, 0, sizeof(ans.materix));
for (int i = 1; i <= f; ++i)
ans.materix[i][i] = 1;
while (b)
{
if (b & 1LL)
ans = mul(ans, a);
a = mul(a, a);
b >>= 1LL;
}
return ans;
}
重载版
struct node
{
LL materix[7][7];
void init()
{
memset(materix, 0, sizeof(materix));
}
void init1()
{
memset(materix, 0, sizeof(materix));
for (int i = 1; i <= ff; ++i)
materix[i][i] = 1;
}
node operator*(const node B) const
{
node C;
C.init();
for (int i = 1; i <= ff; ++i)
{
for (int j = 1; j <= ff; ++j)
{
for (int k = 1; k <= ff; ++k)
{
C.materix[i][j] = (materix[i][k] * B.materix[k][j] + C.materix[i][j]) % mod;
}
}
}
return C;
}
node ksm(node a, long long b)
{
node ans;
ans.init1();
while (b)
{
if (b & 1LL)
ans = ans * a;
a = a * a;
b >>= 1;
}
return ans;
}
}
KMP算法
在一个文本串 S 内查找一个模式串 P 的出现位置
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 7;
int s[MAXN], p[MAXN];
int slen, plen;
int nex[MAXN];
void GetNext()
{
int i, j;
i = 0;
j = nex[0] = -1;
while (i < plen)
{
while (-1 != j && p[i] != p[j])
j = nex[j];
if (p[++i] == p[++j])
nex[i] = nex[j];
else
nex[i] = j;
}
}
int KMP()
{
GetNext();
int i, j;
i = j = 0;
while (i < slen && j < plen)
{
while (-1 != j && s[i] != p[j])
j = nex[j];
i++;
j++;
}
if (j == plen)
return i - j + 1;
else
return -1;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d %d", &slen, &plen);
for (i = 0; i < slen; i++)
cin >> s[i];
for (i = 0; i < plen; i++)
cin >> p[i];
cout << KMP() << endl;
}
return 0;
}
void getNext(string pattern, int *next)
{
int j = -1;
next[0] = -1;
for (int i = 1; i < pattern.size(); ++i)
{
while (j != -1 && pattern[i] != pattern[j + 1])
j = next[j];
if (pattern[i] == pattern[j + 1])
++j;
next[i] = j;
}
}
bool KMP(string text, string pattern)
{
int next[pattern.size()];
getNextval(pattern, next);
for (int i : next)
printf("%d ", i);
int j = -1;
for (int i = 0; i < text.size(); ++i)
{
while (j != -1 && text[i] != pattern[j + 1])
j = next[j];
if (text[i] == pattern[j + 1])
++j;
if (j == pattern.size() - 1)
return true;
}
return false;
}
void getNextval(string pattern, int *next)
{
int j = -1;
next[0] = -1;
for (int i = 1; i < pattern.size(); ++i)
{
if (j != -1 && pattern[i] != pattern[j + 1])
j = next[j];
if (pattern[i] == pattern[j + 1])
++j;
if (j == -1 || pattern[i + 1] != pattern[j + 1])
next[i] = j;
else
next[i] = next[j];
}
}
int KMPcount(string text, string pattern)
{
int next[pattern.size()];
getNextval(pattern, next);
for (int i : next)
printf("%d ", i);
int j = -1, result = 0;
for (int i = 0; i < text.size(); ++i)
{
while (j != -1 && text[i] != pattern[j + 1])
j = next[j];
if (text[i] == pattern[j + 1])
++j;
if (j == pattern.size() - 1)
{
++result;
j = next[j];
}
}
return result;
}
并查集
int father[MAX];
// 初始化并查集
void init(int n)
{
iota(father, father + n, 0);
}
// 寻找根结点并进行路径压缩
int findFather(int x)
{
if (x == father[x])
return x;
int temp = findFather(father[x]);
father[x] = temp;
return temp;
}
// 合并两个集合
void unionSet(int a, int b)
{
int ua = findFather(a), ub = findFather(b);
if (ua != ub)
father[ua] = ub;
}
// 求出并查集中有几个集合
int countRoot(int n)
{
int num = 0;
for (int i = 0; i < n; ++i)
if (father[i] == i)
++num;
return num;
}
最近公共祖先(LCA)问题
倍增法
struct Node
{ // 结点类
int data, father, level; // 权值、父节点在pre中的下标、深度
Node(int d = 0, int f = -1, int l = 0) : data(d), father(f), level(l) {}
};
Node tree[MAX];
// 倍增法解LCA问题,返回最近公共祖先在tree中的下标
int LCA(int a, int b)
{
int ia = -1, ib = -1;
// 找到两个结点在pre数组中的下标
for (int i = 0; i < n; ++i)
{
if (tree[i].data == a)
ia = i;
if (tree[i].data == b)
ib = i;
}
if (ia == -1 || ib == -1) // 结点不在树中,返回-1
return -1;
// 让ia指向深度更大的结点
if (pre[ia].level < pre[ib].level)
swap(ia, ib);
// 将二者调整到同一深度
while (pre[ia].level > pre[ib].level)
ia = pre[ia].father;
// ia,ib同时向上调整,直至二者指向同一结点
while (ia != ib)
{
ia = pre[ia].father;
ib = pre[ib].father;
}
return ia;
}
逆元
费马小定理
long long quickpow(long long a, long long b)
{
if (b < 0)
return 0;
long long res = 1;
a %= mod;
while (b)
{
if (b & 1)
res = (res * a) % mod;
a = (a * a) % mod;
b >= 1;
}
return res;
}
long long inv(long long a)
{
return quickpow(a, mod - 2);
}
扩展欧几里得
long long exgcd(long long a, long long b, long long &x, long long &y)
{
if (!b)
{
x = 1;
y = 0;
return a;
}
else
{
long long r = exgcd(b, a % b, y, x);
y -= x * (a / b);
return r;
}
}
long long inv(long long a)
{
long long x, y;
long long d = exgcd(a, mod, x, y);
return x = (x + mod) % mod;
}
打表
long long inv[N];
void prepare_inv(int n, int M)
{
inv[1] = 1;
for (int i = 2; i <= n; ++i)
{
inv[i] = (long long)(M - M / i) * inv[M % i] % M;
}
}
大数
输入输出
void scan(__int128 &x)
{
int f = 1;
char ch;
x = 0;
if (ch = getchar() == '-')
f = -1;
else
x += ch - '0';
while ((ch == getchar()) >= '0' && ch <= '9')
x = x * 10 + ch - '0';
x *= f;
}
void print(__int128 x)
{
if (!x)
return;
if (x < 0)
putchar('-'), x = -x;
print(x / 10);
putchar(x % 10 + '0');
}
__int128 vi(__int128 a, __int128 b)
{
__int128 mod = b + 2;
__int128 res = 1;
while (b)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
高精度加法
struct node
{
int c[N], l;
node() { l = 0; }
void print()
{
for (int i = l; i >= 1; i--)
printf("%d", c[i]);
printf("\n");
}
} a, b, ans;
node operator+(const node &a, const node &b)
{
node res;
int i;
res.l = max(a.l, b.l);
for (i = 1; i <= res.l; i++)
res.c[i] = a.c[i] + b.c[i];
for (i = 1; i < res.l; i++)
res.c[i + 1] += res.c[i] / 10, res.c[i] %= 10;
while (res.c[res.l] > 9)
res.c[res.l + 1] = res.c[res.l] / 10, res.c[res.l] %= 10, res.l++;
while (res.c[res.l] == 0 && res.l > 1)
res.l--;
return res;
}
高精度减法
struct node
{
int c[N], l;
node() { l = 0; }
void print()
{
for (int i = l; i >= 1; i--)
printf("%d", c[i]);
printf("\n");
}
} a, b, ans;
node operator-(const node &a, const node &b)
{
node res;
res.l = max(a.l, b.l);
for (int i = 1; i <= res.l; i++)
res.c[i] = a.c[i] - b.c[i];
for (int i = 1; i < res.l; i++)
if (res.c[i] < 0)
res.c[i] += 10, res.c[i + 1]--;
while (res.c[res.l] == 0 && res.l > 1)
res.l--;
return res;
}
bool operator>(const node &a, const node &b)
{
if (a.l != b.l)
return a.l > b.l;
for (int i = a.l; i >= 1; i--)
if (a.c[i] != b.c[i])
return a.c[i] > b.c[i];
return 0;
}
高精度乘法
struct node
{
int c[N], l;
node() { l = 0; }
void print()
{
for (int i = l; i >= 1; i--)
printf("%d", c[i]);
printf("\n");
}
} a, b, ans;
node operator*(const node &a, const node &b)
{
node res;
res.l = a.l + b.l - 1;
for (int i = 1; i <= a.l; i++)
for (int j = 1; j <= b.l; j++)
res.c[i + j - 1] += a.c[i] * b.c[j];
for (int i = 1; i < res.l; i++)
res.c[i + 1] += res.c[i] / 10, res.c[i] %= 10;
while (res.c[res.l] > 9)
res.c[res.l + 1] += res.c[res.l] / 10, res.c[res.l] %= 10, res.l++;
while (res.c[res.l] == 0 && res.l > 1)
res.l--;
return res;
}
背包
// 01
int dp[MAXN];
for (int i = 0; i < n; i++)
for (int j = W; j >= w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
printf("%d\n", dp[W]);
// 完全
int dp[MAXN];
for (int i = 0; i < n; i++)
for (int j = w[i]; j <= W; j++)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
printf("%d\n", dp[W]);
// 多重背包
for (int i = 0; i < n; i++)
{
int num = m[i]; // 用来找a
for (int k = 1; num > 0; k < <= 1)
{
int mul = min(k, num);
for (int j = W; j >= w[i] * mul; j--)
{
dp[j] = max(dp[j], dp[j - w[i] * mul] + v[i] * mul);
}
num -= mul;
}
}
printf("%d\n", dp[W]);
并查集
// 并查集(路径压缩)
const int max_n = 100005;
int par[max_n];
void init(int n)
{ // 初始化
for (int i = 1; i <= n; i++)
par[i] = i;
}
int find(int x)
{ // 查找x所在集合的根
if (par[x] != x)
par[x] = find(par[x]); // 递归返回的同时压缩路径
return par[x];
}
void unite(int x, int y)
{ // 合并x与y所在集合
x = find(x);
y = find(y);
par[x] = y;
}
bool same(int x, int y)
{ // x与y在同一集合则返回真
return find(x) == find(y);
}
求二进制下1的个数
int bitcount(unsigned int n)
{
int count = 0;
while (n)
{
count++;
n &= (n - 1);
}
return count;
}
马拉车
public String preProcess(String s) {
int n = s.length();
if (n == 0) {
return "^$";
}
String ret = "^";
for (int i = 0; i < n; i++)
ret += "#" + s.charAt(i);
ret += "#$";
return ret;
}
// 马拉车算法
public String longestPalindrome2(String s) {
String T = preProcess(s);
int n = T.length();
int[] P = new int[n];
int C = 0, R = 0;
for (int i = 1; i < n - 1; i++) {
int i_mirror = 2 * C - i;
if (R > i) {
P[i] = Math.min(R - i, P[i_mirror]);// 防止超出 R
} else {
P[i] = 0;// 等于 R 的情况
}
// 碰到之前讲的三种情况时候,需要利用中心扩展法
while (T.charAt(i + 1 + P[i]) == T.charAt(i - 1 - P[i])) {
P[i]++;
}
// 判断是否需要更新 R
if (i + P[i] > R) {
C = i;
R = i + P[i];
}
}
// 找出 P 的最大值
int maxLen = 0;
int centerIndex = 0;
for (int i = 1; i < n - 1; i++) {
if (P[i] > maxLen) {
maxLen = P[i];
centerIndex = i;
}
}
int start = (centerIndex - maxLen) / 2; //最开始讲的求原字符串下标
return s.substring(start, start + maxLen);
}