数学基础 1
数论
前言
以下大多证明都是扯淡 没有任何正确性可言 有的只是为了做题而做题 随便看看就好
听课的时候的笔记 并不能保证正确性(毕竟听课效率...) 不能保证可读性(毕竟思路混乱...) 其实我还是比较适合抄课件
筛
裸筛
for(int i = 2; i * i <= maxn; i++) if(!a[i])
for(int j = i << 1; j <= maxn; j += i) a[j] = 0;
$O(n loglogn) $
线筛
线性筛中 每个数只会被它最小的质因子筛去
for(int i = 2; i <= maxn; i++)
{
if(!vis[i]) p[++cnt] = i;
for(int j = 1; j <= cnt && p[j] * i <= maxn; j++)
{
vis[p[j] * i] = 1;
if(i % p[j] == 0) break;
}
}
同余
模意义下 加减乘的直接计算不会影响最终结果取模答案
\(P4942\)
\(10^n \equiv 1 ^ n \equiv 1\)
相当于将每一位拆开 再按原数拼起来
/*
Time: 3.31
Worker: Blank_space
Source: P4942 小凯的数字
*/
/*--------------------------------------------*/
#include<cstdio>
#define int long long
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int T, l, r;
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
signed main() {
T = read(); while(T--) {l = read(); r = read(); printf("%lld\n", ((l + r) & 1) ? (r - l + 1 >> 1) % 9 * (l + r) % 9 : (l + r >> 1) % 9 * (r - l + 1) % 9);}
return 0;
}
\(euclid\) (欧几里得)扩展
用于解 \(ax + by = \gcd(a, b)\)
性质: 对于整数 a, b 有无穷解
\(d = ax + by = a(x + \frac{b}{d} * u) + b(y - \frac{a}{d} * u)\)
求任意一组解
原理: 辗转相除
void exgcd(int a, int b, int &d, int &x, int &y) {
if(b) exgcd(b, a % b, d, y, x), y -= x * (a / b);
else d = a, x = 1, y = 0;
}
\(P1516\)
求:
变形
等价于求解关于 \(t\) 和 \(s\) 的不定方程
将一个同余方程转化为欧几里得方程
解这个方程
设
求:
如果 \(d\) 不整除 \(y - x\) 说明方程 \((m - n)t \equiv y - x(mod\ l)\) 无解
否则将 \(t\) 和 \(s\) 扩大 \(\frac{y - x}d\) 倍 可得 \((m - n)t' + ls' = y - x\)
用 \(\frac ld\) 调整 \(t'\)
/*
Time: 3.31
Worker: Blank_space
Source: P1516 青蛙的约会
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int d, t, s;
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
void exgcd(int a, int b, int &d, int &x, int &y) {
if(b) exgcd(b, a % b, d, y, x), y -= x * (a / b);
else d = a, x = 1, y = 0;
}
/*----------------------------------------函数*/
signed main() {
int x = read(), y = read(), m = read(), n = read(), l = read();
if(m < n) Swap(m, n), Swap(x, y);
exgcd(m - n, l, d, t, s); int mod = l / d;
if((y - x) % d) puts("Impossible");
else printf("%lld", ((y - x) / d * t % mod + mod) % mod);
return 0;
}
同余运算法则
取模运算 加减乘不变
同余运算 左右移项不变 乘一个东西不变 同时加减一个东西不变
\(P3951\)
设方程 \(ax + by = c\) 的通解是
由一组特解构成通解 通过调节 \(t\) 找到一组特解 \((x_1, y_1)\) 使 \(0 \leq x_1 \leq b - 1\)
\(x\) 不能为负 也不能使得 \(y\) 为负
\(y > 0\) 有正整数解
\(y < 0\) 无正整数解
找无正整数解的情况 限定 \(y_1 \leq -1\) 那么 \(c = ax_1 + by_1 \leq ax_1 - b \leq ab - a - b\) (已经限定 \(0 \leq x_1 \leq b - 1\))
证明 \(ax + by = ab - a - b\) 没有非负整数解
反证:
若存在满足条件的 \((x_0, y_0)\) 则 \(a(x_0 + 1) + b(y_0 + 1) = ab\)
说明 \(a \mid b(y_0 + 1)\) 即 \(a \mid y_0 + 1\) (\(a\) 与 \(b\) 互质) 则有 \(a \leq y_0 + 1\)
同理: \(b \leq x_0 + 1\)
那么 \(a(x_0 + 1) + b(y_0 + 1) \geq 2ab > ab\)
\(\therefore ax + by = ab - a - b\) 没有非负整数解
前面证明 大于 \(ab - a - b\) 的都可以被表示 后面证明 \(ab - a - b\) 刚好不能被表示
中国剩余定理(CRT)
又称孙子定理
解一坨同余方程组
解线性不定模方程组
\(P4777\)
标准的中剰定理 规定 \(m_i\) 两两互质 但这里没有 所以是扩展(\(EXCRT\))
从两个方程开始
考虑合并
\(x \equiv a_1 (\mod m_1)\) \(x \equiv a_2(\mod m_2)\)
令 \(m = lcm(m_1, m_2)\) \(x \equiv a(\mod m)\)
显然有 \(a = um_1 + a_1 = vm_2 + a_2\)
求 \(a\)
解 \(um_1 - vm_2 = a_2 - a_1\)
可以求得 \(um_1 - vm_2 = 1\) 的解
在扩大到 \(a_2 - a_1\)
实现
exgcd(m1, m2, d, u, v);//d 是最小公约数
m2 = m1 / d * m2;//最小公倍数
u = (a2 - a1) / d * u % m2;//调整u
a2 = (u * m1 % m2 + a1) % m2;//根据上面的式子求a2
将方程组合并成了一个
另一种说法
若
则
设
则
也就是将两个方程组合并成了一个方程
其模数就是 \([m_1, m_2]\)
所以说 上面在扯淡
可求的是
通过这个解出一个 \(u\) 和 \(v\)
如果
有
解释:
所以这是一个很显然的结论
/*
Time: 3.31
Worker: Blank_space
Source: P4777 【模板】扩展中国剩余定理(EXCRT)
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
void exgcd(int a, int b, int &d, int &x, int &y) {
if(b) exgcd(b, a % b, d, y, x), y -= x * (a / b);
else d = a, x = 1, y = 0;
}
int mul(int a, int b, int p) {
int res = 0;
while(b)
{
if(b & 1) res = (res + a) % p;
a = (a << 1) % p;
b >>= 1;
}
return res;
}
/*----------------------------------------函数*/
signed main() {
int n = read() - 1, M = read(), A = read();
while(n--)
{
int m = read(), a = read(), u, v, d;
exgcd(M, m, d, u, v);
u = mul(u, ((a - A) % m + m) % m / d, m);
A += M * u; M = M / d * m; A = (A + M) % M;
}
printf("%lld", A);
return 0;
}
Strange Way to Express Integers
(一道扩展中剩的模板题)
/*
Time: 4.9
Worker: Blank_space
Source: #10213. 「一本通 6.4 例 5」Strange Way to Express Integers
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
void exgcd(int a, int b, int &d, int &x, int &y) {
if(b) exgcd(b, a % b, d, y, x), y -= x * (a / b);
else x = 1, y = 0, d = a;
}
void EXCRT(int n) {
int M = read(), A = read(); bool flag = 0;
for(int i = 1; i <= n; i++)
{
int m = read(), a = read(), x, y, d;
a = ((a - A) % m + m) % m; exgcd(M, m, d, x, y);
if(a % d || flag) {flag = 1; continue;}
x = (x * a / d % m + m) % m;
A += x * M; M = M / d * m; A = (A + M) % M;
}
if(flag) puts("-1"); else printf("%lld\n", A);
}
/*----------------------------------------函数*/
signed main() {
int n; while(~scanf("%lld", &n)) EXCRT(--n);
return 0;
}
\(P3868\)
裸的 \(CRT\)
把上一个题的板子粘过来
/*
Time: 4.9
Worker: Blank_space
Source: P3868 [TJOI2009]猜数字
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, a[12], b[12], m = 1, ans;
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
void exgcd(int a, int b, int &d, int &x, int &y) {
if(b) exgcd(b, a % b, d, y, x), y -= x * (a / b);
else x = 1, y = 0, d = a;
}
int mul(int a, int b, int p, int res = 0) {
for(; b; b >>= 1, a = (a + a) % p) if(b & 1) res = (res + a) % p;
return res;
}
/*----------------------------------------函数*/
signed main() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++) b[i] = read(), m *= b[i], a[i] = (a[i] % b[i] + b[i]) % b[i];
for(int i = 1; i <= n; i++)
{
int _m = m / b[i], d, x, y; exgcd(_m, b[i], d, x, y); x = (x % b[i] + b[i]) % b[i];
ans = ((ans + mul(mul(_m, x, m), a[i], m)) % m + m) % m;
}
printf("%lld", (ans % m + m) % m);
return 0;
}
\(CRT\)
解方程组 \(x \equiv a_i \mod m_i\)
设
那么可以直接给出模 \(M\) 意义下的唯一解
将这一坨东西带到上面的方程中
当 \(i \neq j\) 时
当 \(i = j\) 时
所以
\(P1495\)
/*
Time: 4.9
Worker: Blank_space
Source: P1495 【模板】中国剩余定理(CRT)
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, a[12], b[12], m = 1, ans;
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
void exgcd(int a, int b, int &d, int &x, int &y) {
if(b) exgcd(b, a % b, d, y, x), y -= x * (a / b);
else x = 1, y = 0, d = a;
}
/*----------------------------------------函数*/
signed main() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read(), b[i] = read(), m *= a[i];
for(int i = 1; i <= n; i++)
{
int _m = m / a[i], d, x, y; exgcd(_m, a[i], d, x, y);
ans = ((ans + _m * x * b[i]) % m + m) % m;
}
printf("%lld", ans);
return 0;
}
欧拉函数
欧拉函数 \(\varphi(m)\) 表示不超过 \(m\) 且和 \(m\) 互素的整数的个数 (定义\(\varphi(1) = 1\))
计算
给出 \(m\) 的唯一分解式
欧拉函数的一个性质(欧拉定理)
设 \(m > 1\) 为整数 \(a\) 是与 \(m\) 互素的任一整数 则
欧拉定理的证明
把不超过 \(m\) 的且与 \(m\) 互质的正整数取出构成集合 \(\{x_1, x_2, ..., x_{\varphi (m)}\}\)
设 \(a\) 与 \(m\) 互质 易证集合 \(\{ax_1, ax_2, ..., ax_{\varphi (m)}\}\) 在模 \(m\) 意义下与前一集合相等 (均在模 \(m\) 意义下的缩系)
将集合内所有原数相乘 得到:
整理得(把右边的减过来):
因为 \(\prod_{i = 1}^{\varphi(m)}x_i\) 与 \(m\) 互质 有:
即:
欧拉函数筛
由公式可以推知 \(m\) 的每个质因子 \(p\) 会对 \(m\) 产生贡献
相当于每个质数会对它的倍数产生贡献
\(O(nlogn)\) 进行统计
for(int i = 1; i <= n; i++) e[i] = i;
for(int i = 2; i <= n; i++) if(e[i] == i)
for(int j = i; j <= n; j += i) e[j] = e[j] / i * (i - 1);
欧拉线筛
\(O(n)\) 线筛
线筛的原理是每个数只有其最小的质因数筛去
它也可以 \(O(n)\) 筛除所有积性函数值
分两类讨论
代码
phi[1] = 1;
for(int i = 2; i < maxn; i++)
{
if(!vis[i]) pri[++cnt] = i, phi[i] = i - 1;//1
for(int j = 1; j <= cnt && pri[j] * i < maxn; j++)
{
vis[pri[j] * i] = 1;
phi[pri[j] * i] = (pri[j] - 1) * phi[i];//2
if(!(i % pri[j])) {phi[pri[j] * i] = pri[j] * phi[i]; brea$k;}//3
}
}
代码解释
//1
这里的 \(i\) 是一个质数 \(\varphi(i) = i - 1\)
//2
\(i\) 不是质数 \(pri[j]\) 是其一个质因子 因为欧拉函数是一个积性函数 所以
而 \(pri[j]\) 又是一个质数 所以
所以就有
//3
\(\varphi(pri[j] \times i) = pri[j] \times \varphi(i)\) 这个玩意儿就比较苟了
设:
且 \(x\) 与 \(pri[j]\) 互质
对 \(p\) 为质数 有:
证明:
\(p^k\) 中有 \(p^k\) 个数 其只有 \(p\) 一个质因子 所以其所有的因数都是 \(p\) 的倍数 所以其包含自己在内的因子有 \(p^{k - 1}\) 个 把因子去掉 就有了上式
所以 有:
所以 有:
结合上面设的东西 有:
且:
结合上面这三点 就可以处理欧拉函数了
即(定义 \(p\) 为质数):
- \(\varphi(1) = 1\)
- \(\varphi(p) = p - 1\)
- 若 \((p, x) = 1\) 则 \(\varphi(p \times x) = \varphi(p) \times \varphi(x)\)
- 若 \(p \mid x\) 则 \(\varphi(x \times p) = \varphi(x) \times (p - 1)\)
这也是线筛筛欧拉函数的原理
\(P2158\)
遮住一半
第 \(n + 1\) 列的贡献是 \(\varphi(i)\)
最终答案就是 \(1 + 2\sum_{i = 1}^{n - 1}\varphi(i)\)
裸筛
/*
Time: 4.11
Worker: Blank_space
Source: P2158 [SDOI2008]仪仗队
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, e[B], ans = 1;
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
int main() {
n = read(); if(n == 1) {puts("0"); return 0;}
for(int i = 1; i <= n; i++) e[i] = i;
for(int i = 2; i <= n; i++) if(e[i] == i)
for(int j = i; j <= n; j += i) e[j] = e[j] / i * (i - 1);
for(int i = 1; i < n; i++) ans += e[i] << 1;
printf("%d", ans);
return 0;
}
线筛
/*
Time: 4.11
Worker: Blank_space
Source: P2158 [SDOI2008]仪仗队
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, phi[B], pri[B], cnt, ans;
bool vis[B];
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
int main() {
n = read(); if(n == 1) {puts("0"); return 0;} phi[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!vis[i]) pri[++cnt] = i, phi[i] = i - 1;
for(int j = 1; j <= cnt && i * pri[j] <= n; j++)
{
vis[pri[j] * i] = 1; phi[pri[j] * i] = (pri[j] - 1) * phi[i];
if(!(i % pri[j])) {phi[pri[j] * i] = pri[j] * phi[i]; break;}
}
}
for(int i = 1; i < n; i++) ans += phi[i];
printf("%d", ans << 1 | 1);
return 0;
}
乘法逆元
鸽子合集
模数为质数 费马小定理
其他:
\(O(n)\) 递推
inv[1] = 1;
inv[i] = (mod - mod / i) * inv[i % mod] % mod;
证明
设:
有:
两边同乘 \(i^{-1}r^{-1}\) 有:
即:
证毕
卡特兰数
题目模型
- 凸 \(n + 2\) 边形剖分数
- \(n\) 对合法括号方案数
- \(n\) 个元素出栈方案数
- \(n\) 个节点的二叉树个数
- \(n \times n\) 的放个从左下到右上不穿过对角线的方案数
- 圆周上 \(2n\) 个顶点配对相连 线段两两不想交方案数
- \(2n\) 个人 一半带了 \(5\) 元 另一半带了 \(10\) 元 票价 \(5\) 元 不利用外界的前找完所有人的前的站队方案数
- ... ...
凸 \(n + 2\) 边形剖分计数
用 \(n\) 条连接顶点的线段把凸 \(n + 2\) 边形剖分成 \(n\) 个三角形的方案数
人为定义 \(h_0 = 1\)
给 \(n + 2\) 个顶点标号 (\(0 - n + 1\))
考虑一个方案中与连接 \(0\) 号点相连的最小的点 \(i\)
\(0 - i\) 线剖分了多少凸四边形
一边不连 \(0\) 号点 方案数为 \(h_{i - 2}\) 另一侧可以连 \(0\) 号点 方案数是 \(h_{n - i + 1}\)
总方案
合法括号计数
用 \(n\) 对括号可以组成多少合法括号串
人为定义 \(h_0 = 1\)
\(n\) 对括号任意放置的方案数为 \({2n \choose n}\)
考虑非法括号方案 找到第一个非法位置 把该位置后面的括号取反
得到一个有 \(n + 1\) 个右括号的序列 方案数为 \({2n \choose n + 1}\) 每一个 \(n + 1\) 个右括号的序列都对应了一个非法的原序列
得到
化简
故 有
有
其他
\(n\) 个元素出栈方案数
入栈 = 左括号
出栈 = 右括号
\(n \times n\) 的方格从左下到右上不穿过对角线的方案数
右 = 左括号
上 = 右括号
圆周上 \(2n\) 个顶点配对相连 线段两两不相交方案数
给 \(2n\) 个顶点标号
连向点标号比自己大的点 = 左括号
连向点标号比自己小的点 = 右括号
\(2n\) 个人 一半带了 \(5\) 元 另一半带了 \(10\) 元 票价 \(5\) 元 不利用外界的前找完所有人的前的站队方案数
\(5\) 元 = 左括号
\(10\) 元 = 右括号
二叉树计数
人为定义 \(h_0 = 1\)
只考虑根节点 若左子树中有 \(i\) (\(0 \leq i \leq n - 1\)) 个点 则右子树有 \(n - i - 1\) 个点
P1044 栈
\(n \leq 18\) ???
\(n \leq 5000\) \(h_n = \sum_{i = 0}^{n - 1}h_i \times h_{n - 1 - i}\)
\(n \leq 10 ^ 5\ \ h_n = h_{n - 1}\frac{4n - 2}{n + 1}\)
\(n \leq 10^9\ \ h_n = {2n \choose n} - {2n \choose n + 1}\) , \(Lucas\)
话说 \(Lucas\) 是个啥
康托展开
康托展开是全排列序列到自然数的一一映射 该自然数可以代表该序列在全排列中的排名
(正) 康托展开 : 全排列 \(\to\) 名次
考虑有多少个排列可以比当前排列小
(逆) 康托展开 : 名次 \(\to\) 全排列
可以快速知道每一位 后面比当前小的数的个数
\(P2525\)
(菜到只配做入门题)
先康了
然后 \(-1\)
再逆康
/*
Time: 4.11
Worker: Blank_space
Source: P2525 Uim的情人节礼物·其之壱
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<vector>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, m, f[12], _a[12], a[12];
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
int cantor() {
int res = 0, x;
for(int i = 0; i < n; i++)
{
x = 0;
for(int j = i + 1; j < n; j++) if(_a[i] - _a[j] > 0) x++;
res += x * f[n - i - 1];
}
return res;
}
int incantor(int k) {
int x; std::vector <int> v;
while(!v.empty()) v.pop_back();
for(int i = 1; i <= n; i++) v.push_back(i);
for(int i = 1; i < n; i++)
{
a[i] = v[k / f[n - i]]; v.erase(v.begin() + k / f[n - i]);
k %= f[n - i];
}
a[n] = v[0];
}
/*----------------------------------------函数*/
int main() {
n = read(); f[1] = 1;
for(int i = 0; i < n; i++) _a[i] = read();
for(int i = 2; i <= 10; i++) f[i] = f[i - 1] * i;
int t = cantor(); if(!t) puts("ERROR");
incantor(t - 1);
for(int i = 1; i <= n; i++) printf("%d ", a[i]);
return 0;
}
\(P5367\) 【模板】康托展开
\(n^2\) 被卡了
发现多出来的那个 \(O(n)\) 就是一个找还没有出现过的 \(k\) 大值的过程 随便写个数据结构
像什么权值树状数组 权值线段树 平衡.. 没事了
/*
Time: 4.11
Worker: Blank_space
Source: P5367 【模板】康托展开
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define lowbit(x) ((x) & -(x))
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 998244353;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, a[C], ans, f[C], t[C];
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
void add(int x, int k) {for(int i = x; i <= n; i += lowbit(i)) t[i] += k;}
int sum(int x, int res = 0) {for(int i = x; i; i -= lowbit(i)) res += t[i]; return res;}
/*----------------------------------------函数*/
signed main() {
n = read(); f[0] = 1;
for(int i = 1; i <= n; i++) f[i] = f[i - 1] * i % mod, add(i, 1);
for(int i = 1; i <= n; i++)
{
a[i] = read();
ans = (ans + (sum(a[i]) - 1) * f[n - i] % mod) % mod;
add(a[i], -1);
}
printf("%lld", ans + 1);
return 0;
}
简单计数
第一类斯特林数
\(s_{n, m}\) 表示的是将 \(n\) 个不同的元素构成 \(m\) 个圆排列的方案数
考虑插入第 \(n\) 个元素 可以直接递推
- 自成一环 环数加一
- 插在某个元素后面 环数不变
注意边界
第二类斯特林数
\(s_{n, m}\) 表示的是吧 \(n\) 个不同元素划分到 \(m\) 个集合中的方案数
考虑插入第 \(n\) 个元素
- 自成一集合 集合数加一
- 加入某集合 集合数不变
注意边界
\(P1287\)
如果盒子相同 就是第二类斯特林数 但是盒子不同 所以需要乘上盒子的全排列
答案是
或者 \(dp\)
错排计数
满足 \(a_i \ne i\ (1 \leq i\leq n)\) 的排列的个数
考虑第 \(n\) 个位置是 \(i\)
- 第 \(i\) 个位置上恰好是 \(n\)
- 第 \(i\) 个位置上不是 \(n\) 把 \(n\) 看成 \(i\)
\(P4071\)
把稳定的拿出来 剩下的就是错排
/*
Time: 4.11
Worker: Blank_space
Source: P4071 [SDOI2016]排列计数
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int T, n, m, d[C], f[C], inv[C], ans;
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
int mul(int a, int b) {int res = 1; for(; b; b >>= 1, a = a * a % mod) if(b & 1) res = res * a % mod; return res;}
/*----------------------------------------函数*/
signed main() {
T = read(); d[2] = 1; f[1] = 1; inv[1] = 1;
for(int i = 3; i <= C - 2; i++) d[i] = (i - 1) * (d[i - 1] + d[i - 2]) % mod;
for(int i = 2; i <= C - 2; i++) f[i] = f[i - 1] * i % mod, inv[i] = mul(f[i], mod - 2);
while(T--)
{
n = read(); m = read();
if(n - m == 1) {puts("0"); continue;} if(m == n) {puts("1"); continue;} if(!m) {printf("%lld\n", d[n]); continue;}
ans = f[n] * inv[m] % mod * inv[n - m] % mod * d[n - m] % mod;
printf("%lld\n", ans);
}
return 0;
}
—— \(END\)