约数个数定理:求解一个大于1的正约数的个数和。
对于大于1的正数分解质数因数 :
正约个数总和为:
详细介绍参考:百度百科
问题:求解正整数378000共有多少个正约数?
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
#define ll long long
int zhi[N], sum[N], cnt;
bool vis[N];
void get_zhi(int n) { //线性求解质数
for (int i = 2; i <= n; ++i) {
if (!vis[i])zhi[cnt++] = i;
for (int j = 2 * i; j <= n; j += i) {
vis[j] = true;
}
}
}
int main()
{
int n = 378000;
get_zhi(n);
for (int i = 0; i < cnt; ++i) {
int p = zhi[i];
int k = 0;
while (n%p==0) {
n /= p;
k++;
}
sum[i] = k;
}
ll ans = 1;
for (int i = 0; i < cnt; ++i) {
ans *= (sum[i] + 1);
}
cout << ans;
return 0;
}
其他公式待整理
欧几里得:
ax+by=c;
若a,b互为质数,则x,y一定有解且无穷多个。
x,y>=0,使得ax+by=c无解的c个数有限,
且 存在max(c|c导致方程无解)=a*b-a-b
欧几里得扩展定理:
a mod b==a-a/b*b 其中(a/b向下取证)
洛谷--P1082 [NOIP2012 提高组] 同余方程
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a, b, x, y;
//哦欧几里得扩展定理 可以求解逆元
ll exgcd(ll a, ll b, ll& x, ll& y) {
if (!b) {
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll t = x;
x = y;
y = t - (a / b) * y;
return d;
}
int main() {
cin >> a >> b;
ll g = exgcd(a, b, x, y);
cout << (x + b) % b;
return 0;
}
线性求逆元:
//洛谷--P3811 【模板】乘法逆元
//线性求逆元法
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n, p, inv[3000005];
int main() {
scanf_s("%lld%lld", &n, &p);
inv[1] = 1;
printf("%lld\n", inv[1]);
for (ll i = 2; i <= n; ++i) {
inv[i] = (p - p / i) * inv[p % i] % p;
//技巧 其中p==m -p/i%p==(p-p/i)%m==p%m-p/i%m==0-p/i%p 防止出现负数
printf("%lld\n", inv[i]);
}
return 0;
}
快速幂求逆元:
//洛谷--P2613 【模板】有理数取余
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 19260817;
//快读
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch>'9') {
if (ch == '-')f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x * 10 % mod + (ch - '0')) % mod; // x*10==x<<1+x<<3
ch = getchar();
}
return x%mod*f;
}
//快速幂 log
ll qpow(ll a, ll b, ll m) {
a %= m;
ll res = 1;
while (b > 0) {
if (b & 1)res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
ll a, b;
int main() {
a = read(), b = read();
if (b == 0)cout << "Angry!";
else cout << a * qpow(b, mod - 2, mod) % mod;
return 0;
}
卡特兰数(组合数学):
//洛谷--P1044 [NOIP2003 普及组] 栈
// 数学组合 卡特兰数 模板
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,f[25];
int main() {
cin >> n;
f[1] = 1;
for (ll i = 2; i <= n; ++i)
f[i] = f[i - 1] * (4 * i - 2) / (i + 1); 卡特兰数 模板
cout << f[n];
return 0;
}
//洛谷--P1375 小猫
//定义数学组合
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const ll MAX = 1000005;
ll n, f[MAX], inv[MAX];
ll qpow(ll a, ll b, ll m) {
a %= m;
ll res = 1;
while (b) {
if (b & 1)res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
void pre() {
f[0] = 1;
for (int i = 1; i <= 1e6; ++i) {
f[i] = f[i - 1] * i % mod;
inv[i] = qpow(f[i], mod - 2, mod);
}
}
ll c(ll n, ll m) {
return f[n] * inv[m] % mod * inv[n - m] % mod;
//定义 n!/(m!*(n-m)!)
}
int main() {
pre();
cin >> n;
cout << c(2 * n, n) * qpow(n + 1, mod - 2, mod) % mod;
return 0;
}
错排:
//洛谷--P4071 [SDOI2016]排列计数
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const ll MAX = 1e6 + 5;
ll t, n, m, f[MAX], d[MAX], inv[MAX];
ll qpow(ll a, ll b, ll m) {
a %= m;
ll res = 1;
while (b > 0) {
if (b & 1)res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
void pre() {
f[0] = 1;
for (ll i=1; i <= 1e6; ++i) {
f[i] = f[i - 1] * i % mod;
inv[i] = qpow(f[i], mod - 2, mod);
}
inv[0] = inv[1];
d[1] = 0, d[2] = 1;
for (ll i = 3; i <= 1e6; ++i) { //错排
d[i] = (i - 1) * (d[i - 1] + d[i - 2]) % mod;
}
}
ll c(ll n, ll m) {
return f[n] * inv[m] % mod * inv[n - m] % mod;
}
int main() {
pre();
scanf_s("%lld", &t);
while (t--) {
scanf_s("%lld%lld", &n, &m);
if (n == m)printf("1\n");
else printf("%lld\n", c(n, m) * d[n - m] % mod);
}
return 0;
}
容斥原理:
洛谷--P1450 [HAOI2008] 硬币购物
容斥原理
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n, dp[100005], c[5], d[5], s,ans;
int main() {
cin >> c[1] >> c[2] >> c[3] >> c[4] >> n;
dp[0] = 1;
for (int i = 1; i <= 4; ++i) {
for (int j = c[i]; j <= 1e5; ++j) {
dp[j] += dp[j - c[i]];
}
}
while (n--) {
cin >> d[1] >> d[2] >> d[3] >> d[4] >> s;
ans=0;
for (int i = 1; i < (1 << 4); ++i) {
ll f = -1, sum = 0;
for (int j = 1; j <= 4; ++j) {
if ((1 << (j - 1)) & i)
f *= -1, sum += c[j] * (d[j] + 1);
}
if (s >= sum)ans += f * dp[s - sum];
}
cout << dp[s]-ans << endl;
}
return 0;
}
全排列算法:无重复元素
/*递归回溯生产全排列,使用于无重复元素的情况*/
/*考虑第k位,前面已经排列*/
f[0];
void f(int k) {
if (k == 9) { //一种排列
if (check())
ans++;
}
//从k往后的每个数字都可以放在k位
for (int i = k; i < 9; ++i) {
{ int t = a[k]; a[k] = a[i]; a[i] = t; }
f(k + 1);
{ int t = a[k]; a[k] = a[i]; a[i] = t; }
}
}
数组约分:
法一:
long long C(int n, int m) { //n=6 m=2
if(m < n-m) m = n-m; //n=6 m=4
long long ans = 1;
for(int i = m+1; i <= n; i++) ans *= i; // 5*6
for(int i = 1; i <= n-m; i++) ans /= i; // /1/2
return ans; //15
}
法二:
long long C(int n, int m) {
Long long res=1;
For(int i=a,j=1;j<=b;++j,--i){
Res*=res*i/j;
}
Return res;
}
分割平面、空间问题 数学公式
(1) n条直线最多分平面问题
题目:n条直线,最多可以把平面分为多少个区域。
公式:f(n)=n(n+1)/2+1
(2)折线分平面
公式:f(n)=2n^2-n+1
(3)封闭曲线分平面
公式:f(n)=n^2-n+2
(4)平面分割空间问题
公式:f(n)=(n^3+5n)/6+1
两点式直线方程:
(y1-y2) * x +(x2-x1) * y +( x1 * y2 - x2 * y1)=0
并查集
/*蓝桥杯 蓝桥侦察 并查集 */
// https://www.lanqiao.cn/problems/1136/learning/
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n, m, far[N << 1];
int find(int x) {
if (x == far[x]) return x;
return far[x] = find(far[x]);
}
void Union(int x, int y) {
int tx = find(x), ty = find(y);
if (tx != ty)far[tx] = ty;
}
int main() {
int res = 0, pos = 0;
cin >> n >> m;
for (int i = 1; i <= 2 * n; ++i) far[i] = i;
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
if (res) continue;
{
if (find(x) == find(y) || find(x + n) == find(y + n))
res = x, pos = i;
Union(x, y + n), Union(x + n, y);
}
}
cout << res << endl;;
return 0;
}
/*
测试:
------------------
输入:
4 5
1 2
1 3
2 3
3 4
1 4
输出:
2
---------------------
*/
/*并查集*/
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;//数据太大 要开ll
const int N = 100010;
int p[N];
int find(int x) {
if (p[x] != x) {
p[x] = find(p[x]);
}
return p[x];
}
int main() {
int n, m, x;
cin >> n >> m >> x;
for (int i = 1; i <= n; i++) {
p[i] = i;
}
while (m--) {
int a, b;
cin >> a >> b;
p[find(a)] = find(b);
}
while (x--) {
int a, b;
cin >> a >> b;
if (find(a) == find(b)) {
puts("Yes");
}
else {
puts("No");
}
}
return 0;
}
前N相求和的树状法
#include<bits/stdc++.h>
using namespace std;
int lowbit(int n) {
return n - (n & (n - 1));
}
void update(int n, int i, int v, int f[]) {
for (int k = i; k <= n; k += lowbit(k)) {
f[k] += v;
}
}
int getsum(int c[], int i) {
int sum = 0;
for (int k = i; k >= 1; k -= lowbit(k)) {
sum += c[k];
}
return sum;
}
int main() {
int arr[] = { 1,2,3,4,5,6,7,8,9 };
int f[9];
memset(f, 0, sizeof(f));
for (int i = 0; i < 9; ++i) {
update(9, i + 1, arr[i], f);
}
cout << getsum(f, 0) << endl; // 0
cout << getsum(f, 1) << endl; // 1
cout << getsum(f, 9) << endl; // 45
return 0;
}
一位数组求和公式(动态规划、前缀和):
#include<bits/stdc++.h>
using namespace std;
int dp[100],s[100];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> s[i];
}
for (int i = 1; i <= n; ++i) {
dp[i] = dp[i - 1] + s[i];
}
int m;
cin >>m;
cout << dp[m] << endl; //局部求和公式
return 0;
}
/*
10
1 2 3 4 5 6 7 8 9 10
5
15
*/
二维数组求和公式(动态规划、前缀和):
#include<bits/stdc++.h>
using namespace std;
int dp[100][100], s[100][100];
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> s[i][j];
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + s[i][j]; //二维数组求和公式
}
}
int x1, x2, y1, y2;
cin >> x2 >> y2 >> x1 >> y1;
cout << dp[x2][y2] - dp[x2 - 1][y1 - 1] - dp[x1 - 1][y2] + s[x1 - 1][y1 - 1] << endl; //局部求和公式
return 0;
}
/*
3 4
1 2 3 4
5 6 7 8
9 10 11 12
2 2 0 0
14
*/
三角形面积公式:
分解质因数
#include<bits/stdc++.h>
using namespace std;
int main() {
int x;
cin >> x;
for (int i = 2; i <= x; ++i) {
while (x != i) {
if (x % i == 0) {
cout << i << "*";
x /= i;
}
else break;
}
}
cout << x << endl;
return 0;
}
/*
测试:
-------------
输入:
160
输出:
2*2*2*2*2*5
--------------
输入:
10
输出:
2*5
--------------
*/
裴蜀定理(或贝祖定理):
约定个数定理:https://baike.baidu.com/item/%E7%BA%A6%E6%95%B0%E4%B8%AA%E6%95%B0%E5%AE%9A%E7%90%86/4926961
1约数个数定理
则n的正约数的个数就是。
其中a1、a2、a3…ak是p1、p2、p3,…pk的指数。
2定理简证
首先同上,n可以分解质因数:n=p1^a1×p2^a2×p3^a3*…*pk^ak,
由约数定义可知p1^a1的约数有:p1^0, p1^1, p1^2......p1^a1 ,共(a1+1)个;同理p2^a2的约数有(a2+1)个......pk^ak的约数有(ak+1)个。
故根据乘法原理:n的约数的个数就是(a1+1)(a2+1)(a3+1)…(ak+1)。
3例题
例题:正整数378000共有多少个正约数?
解:将378000分解质因数378000=2^4×3^3×5^3×7^1
由约数个数定理可知378000共有正约数(4+1)×(3+1)×(3+1)×(1+1)=160个。