数论

同余

设n是给定的正整数,若整数a、b满足n|(a−b),则称a和b模𝑛同余,记作a≡b(mod n)
在模的意义下,加减乘的直接计算都不会影响最终结果取模的答案
12−8−11≡2−3−1≡−2≡3(mod 5)
12×8×11≡2×3×1≡6≡1(mod 5)

\(10^n \equiv 1^n \equiv 1(mod \ 9)\)

小凯的数字

#include <cstdio>
#include <iostream>
#define orz cout << "AK IOI" <<"\n"
#define int long long

using namespace std;

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 q; 
signed main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    q = read();
    for(int i = 1; i <= q; i++)
    {
    	int l = read(), r = read();
    	int a = (r - l + 1) % 9;
    	int ans = a * (l % 9) % 9 + (a) * (a - 1) % 9 * 5 % 9;
    	printf("%lld\n", ans % 9);
	}
	return 0;
}

扩展欧几里得

首先贝祖定理: 如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。

换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。(可以来判断一个这样的式子有没有解)
板子:

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;y=0;
        return a;  
    }
    int r=exgcd(b,a%b,x,y);
    int temp = y;    //把x y变成上一层的
    y = x-(a/b)*y;
    x = temp;
    return r;   
}

原文链接:https://blog.csdn.net/destiny1507/article/details/81750874

欧拉函数

欧拉函数表示不超过m,且与m互素的正整数的个数。

方法:

  1. m < \(10^7\) 枚举互质。

  2. 给出m的唯一分解式

    (唯一分解式即算数基本定理,每一个大于1 的整数若不是质数都可以写成有限多个质因子的乘积且经过适当排序其写法唯一。)

    \(m = \prod p_i ^{a_i}\) \(eg:24 = 2 ^ 3 \times 3\)

    \(o(m) = m \prod (1 - \frac{1}{p_i})\) = 24 \(\times (1 - \frac{1}{2})(1 - \frac13)\)

欧拉函数有一个欧拉定理:

设 m > 1为整数,a是与m互素的任一整数,则

\(a ^ {o(m)} \equiv 1(mod \ m)\) (没有什么用???!!!)

欧拉函数筛

\(o(m) = m \prod (1 - \frac{1}{p_i})\)

由公式可知, m的每一个质因子p会对m产生贡献,

那么就相当于每个质数会对它的倍数产生贡献。

\(O(n \ log n)\)

线性筛的原理是每个数只由其最小的质因数筛去,意味着也能\(O(n)\)筛出所有积性函数值。(积性函数:f(mn) = f(m) \(\times\) f(n) 其中m,n互质,m,n不互质,完全积性函数)

需要分两类讨论。

void getphi()
{
	phi[1] = 1;
	for(int i = 2; i < maxn; i++)
	{
		if(!vis[i]) pri[tot++] = i, phi[i] = i - 1;
		for(int j = 0, y; j < tot && (y = pri[j] * i) < maxn; j++)
		{
			vis[y] = 1;
			phi[y] = (pri[j] - 1) * phi[i];
			if(i % pri[j] == 0)
			{
				phi[v] = phi[i] * pri[j];
				break;
			}
		}
	}
} 

\(O(n)\)

仪仗队

/*
统计有多少个互质点 
*/
#include <cstdio>
#include <iostream>
#define orz cout << "AK IOI" <<"\n"

using namespace std;
const int maxn = 2e6 + 10;

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 n, ans, a[maxn];
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);那孩子要呆到几点。 
    n = read();
    if(n == 1) {printf("0"); return 0;}
    for(int i = 1; i <= n; i++) a[i] = i;
    for(int i = 2; i <= n; i++)
    {
    	if(a[i] == i)//判断i为质数 
    	{
    		for(int j = i; j <= n; j += i)
    			a[j] = a[j] * (i - 1) / i;
		}
	}
	for(int i = 1; i < n; i++)	ans += a[i]; 
    printf("%d", ans  + 1);
	return 0;
}

乘法逆元

在模意义下,加法减法乘法都不会对结果产生影响,乘法逆元是为了解决模意义下除法问题。

已知a|b,如何快速的求$ \frac ba(mod \ m)\( 设\) \frac ba \equiv r(mod \ m)\( 找一个\)a^{-1}$使得 \(a \times a^{-1} \equiv (mod \ m)\)
\(b * a^{-1} \equiv r * a * a ^ {-1} \equiv r \equiv \frac ba (mod \ m)\)

怎么找\(a ^ {-1}\)?

  1. 欧几里得扩展
  2. 欧拉函数(费马小定理)&快速幂
#include <cstdio>
#include <iostream>
#define int long long 

using namespace std;

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 + ch - '0'; ch = getchar();}
	return x * f;
}
int n, p, x, y, a[5000010];
signed main()
{
	//freopen(".in", "r", stdin);
	//freopen(".out", "w", stdout);
	n = read(); p = read();
	a[0] = 0;
	a[1] = 1;
	printf("1\n");
	for(int i = 2; i <= n; i++)
	{
	   a[i] = (p - p / i ) * a[p % i] % p;
     printf("%lld\n",a[i]); 
	}
	return 0;
} 

同余方程

#include <cstdio>
#include <iostream>
#define orz cout << "AK IOI" <<"\n"

using namespace std;

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 a, b, d, x, y; 
void exgcd(int a, int b, int &d, int &x, int &y)//扩展欧几里得 
{
	if(b == 0){ d = a; x = 1; y = 0;}
	else {exgcd(b, a % b, d, y, x); y -= (a / b) * x;}
}
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    a = read(), b = read();
    exgcd(a, b, d, x, y);
    int ans = (x % b + b) % b;
    printf("%d", ans);
	return 0;
}

卡特兰数(不会啊啊啊)

模型:

  1. 凸n + 2边形剖分数

    用n条连接顶点的线段把凸n + 2边剖成n个三角形的方案数

  2. n对合法括号方案数

  3. n个元素出栈方案数

  4. n个节点的二叉树个数

  5. n\(\times\)n的方格从左下到右上不穿过对角线的方案数

  6. 圆周上2n个顶点配对连线,线段两两不相交的方案数

  7. 2n个人,一半带了5元,另一半带了10元,票价5元,不利用外界的钱找完所有人的钱的站队的方案数

康托展开

康托展开是全排列序列到自然数的一一映射,该自然数可以代表该序列在全排列中的排名(从零开始)

正康拓展开:全排列\(\rightarrow\) 名次

逆康拓展开:名次\(\rightarrow\) 全排列

简单计算

第一类斯特林数:

s(n,m)表示将n个不同元素,构成m个圆排列的方案数。

考虑插入第n个元素:

自成一环,环数加一:s(n - 1, m - 1)

插在某元素后边,环数不变:(n - 1)$\times $s(n - 1, m)

s(n, m) = (n - 1)\(\times\)s(n - 1, m) + s(n - 1, m - 1);

第二次斯特林数

s(n, m)表示的是把n个不同元素分到m个集合的方案数

考虑插入第n个数

自成一集合,集合数加一:s(n - 1,m - 1)

加入某集合, 集合数不变:m\(\times\)s(n - 1, m)

边界:n >= m s(0, 0) = 1 s(0, i) = 0

错排记数

满足\(a_i != i\)的数列的个数

考虑第n个位置是i

第i个位置上恰好是n:\(d_{n - 2}\)

第i个位置上不是n,把n看成i, \(d_{n - 1}\)

\(d_n = (n - 1)(d_{n - 1} + d{n - 2}) (d_1 = 0, d_2 = 1)\)

排列计数

#include <cstdio>
#include <iostream>
#define orz cout << "AK IOI" <<"\n"
#define int long long

using namespace std;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;

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 T, n, m, f[maxn], d[maxn], in[maxn];
int power(int a, int b)
{
	int ans = 1;
	while(b)
	{
		if(b & 1) ans = a * ans % mod;
		a = a * a % mod;
		b >>= 1;	
	}
	return ans;	
} 
void work()
{
	f[0] = 1;
	for(int i = 1; i < maxn; i++)
	{
		f[i] = f[i - 1] * i % mod;
		in[i] = power(f[i], mod - 2);
	}
	d[1] = 0, d[2] = 1, d[3] = 2;
	for(int i = 4; i < maxn; i++)
	d[i] = (i - 1) * (d[i - 1] + d[i - 2]) % mod; 
}
signed main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    T = read();
    work();
    while(T--)
    {
    	n = read(), m = read();
    	if(n - m == 1) printf("0\n");
    	else if(m == n) printf("1\n");
    	else if(m == 0) printf("%lld\n", d[n]);
    	else printf("%lld\n", f[n] * in[m] % mod * in[n - m] % mod * d[n - m] % mod);
	}
	return 0;
}

posted @ 2021-04-18 20:30  _程门立雪  阅读(105)  评论(2编辑  收藏  举报