数学知识

矩阵乘法

斐波那契前 n 项和

#include <bits/stdc++.h>
using namespace std;
int c, mod;
struct matrix
{
	int n, m, a[4][4];
	matrix operator * (const matrix &x) const
	{
		matrix y;
		y.n = n, y.m = x.m;
		for (int i = 1; i <= n; ++ i)
		{
			for (int j = 1; j <= x.m; ++ j)
			{
				y.a[i][j] = 0;
				for (int k = 1; k <= m; ++ k)
				{
					y.a[i][j] = (y.a[i][j] + (long long)a[i][k] * x.a[k][j] % mod) % mod;
				}
			} 
		}
		return y;
	}
};
matrix quick(matrix x, int b)
{
	matrix res;
	res.n = x.m, res.m = x.m;
	for (int i = 1; i <= res.n; ++ i)
	{
		for (int j = 1; j <= res.m; ++ j)
		{
			if(i == j) res.a[i][j] = 1;
			else res.a[i][j] = 0;
		}
	}
	while(b)
	{
		if(b & 1) res = res * x;
		x = x * x;
		b >>= 1;
	}
	return res;
}
int main()
{
	scanf("%d %d", &c, &mod);
	matrix x, y;
	x.n = 1, x.m = 3, y.n = 3, y.m = 3;
	x.a[1][1] = 1, x.a[1][2] = 1, x.a[1][3] = 1;
	
	y.a[1][1] = 0, y.a[1][2] = 1, y.a[1][3] = 0;
	y.a[2][1] = 1, y.a[2][2] = 1, y.a[2][3] = 1;
	y.a[3][1] = 0, y.a[3][2] = 0, y.a[3][3] = 1;
	
	x = x * quick(y, c - 1);
	printf("%d", x.a[1][3]);
	return 0;
}

佳佳的斐波那契

#include <bits/stdc++.h>
using namespace std;
int c, mod;
struct matrix
{
	int n, m, a[6][6];
	matrix operator * (const matrix &x) const
	{
		matrix y;
		y.n = n, y.m = x.m;
		for (int i = 0; i < n; ++ i)
		{
			for (int j = 0; j < x.m; ++ j)
			{
				y.a[i][j] = 0;
				for (int k = 0; k < m; ++ k)
				{
					y.a[i][j] = (y.a[i][j] + (long long)a[i][k] * x.a[k][j] % mod) % mod;
				}
			} 
		}
		return y;
	}
};
matrix quick(matrix x, int b)
{
	matrix res;
	res.n = x.m, res.m = x.m;
	for (int i = 0; i < res.n; ++ i)
	{
		for (int j = 0; j < res.m; ++ j)
		{
			if(i == j) res.a[i][j] = 1;
			else res.a[i][j] = 0;
		}
	}
	while(b)
	{
		if(b & 1) res = res * x;
		x = x * x;
		b >>= 1;
	}
	return res;
}
int main()
{
	scanf("%d %d", &c, &mod);
	matrix x, y;
	x.n = 1, x.m = 5, y.n = 5, y.m = 5;
	x.a[0][0] = 1, x.a[0][1] = 1, x.a[0][2] = 1, x.a[0][3] = 2, x.a[0][4] = 1;
	
	y.a[0][0] = 0, y.a[0][1] = 1, y.a[0][2] = 0, y.a[0][3] = 2, y.a[0][4] = 0;
	y.a[1][0] = 1, y.a[1][1] = 1, y.a[1][2] = 0, y.a[1][3] = 1, y.a[1][4] = 0;
	y.a[2][0] = 0, y.a[2][1] = 0, y.a[2][2] = 0, y.a[2][3] = 1, y.a[2][4] = 0;
	y.a[3][0] = 0, y.a[3][1] = 0, y.a[3][2] = 1, y.a[3][3] = 1, y.a[3][4] = 1;
	y.a[4][0] = 0, y.a[4][1] = 0, y.a[4][2] = 0, y.a[4][3] = 0, y.a[4][4] = 1;
	x = x * quick(y, c - 1);
	printf("%d", x.a[0][4]);
	return 0;
}

GT考试

我真的无语了,这个题目简化版是做过的,完全没有印象了捏

状态表示\(f[i][j]\):

集合:所有长度为i,且不包含S串,且末尾部分与S的前缀匹配最大长度是j的所有字符串的集合

属性:集合里面元素的总和。

状态计算(集合划分):看\(f[i][j]\)可以构造出来哪些\(f[i +1][k]\)。枚举最后一位是什么数字,然后用kmp去计算\(j\)变成了什么,这就是新的状态。

一般的\(dp[i][j]\)都可以变成\(F[i] = A * F[i - 1]\)的形式,其中A是一个矩阵。如果A是固定的,并且j不是很大(矩阵的时间复杂度是三次方)的话,那就可以用快速幂来算了。

#include <bits/stdc++.h>
using namespace std;
const int N = 22;
char s[N];
int nxt[N], c, d, mod;
void kmp()
{
	for (int i = 0, j = 2; j <= d; ++ j)
	{
		while(i && s[i + 1] != s[j]) i = nxt[i];
		if(s[i + 1] == s[j]) i ++;
		nxt[j] = i;
	}
	return ;
}
struct matrix
{
	int a[N][N], n, m;
	matrix operator * (const matrix &x) const
	{
		matrix y;
		y.n = n, y.m = x.m;
		for (int i = 0; i < n; ++ i)
		{
			for (int j = 0; j < x.m; ++ j)
			{
				y.a[i][j] = 0;
				for (int k = 0; k < m; ++ k) y.a[i][j] = (y.a[i][j] + a[i][k] * x.a[k][j] % mod) % mod;
			}
		}	
		return y;
	} 
};
matrix quick(matrix x, int b)
{
	matrix res;
	res.n = x.m, res.m = x.m;
	for (int i = 0; i < res.n; ++ i)
	{
		for (int j = 0; j < res.m; ++ j) 
		{
			if(i == j) res.a[i][j] = 1;
			else res.a[i][j] = 0;
		}
	}
	while(b)
	{
		if(b & 1) res = res * x;
		x = x * x;
		b >>= 1;
	}
	return res;
}
int main()
{
	scanf("%d %d %d", &c, &d, &mod);
	scanf("%s", s + 1);
	kmp();
	matrix x, y;
	x.n = 1, x.m = d, y.n = d, y.m = d;
	for (int i = 0; i < d; ++ i)
	{
		x.a[0][i] = 0;
		for (int j = 0; j < d; ++ j) y.a[i][j] = 0;
	}
	x.a[0][0] = 9, x.a[0][1] = 1;
	for (int i = 1; i <= d; ++ i) s[i] -= '0';
	for (int st = 0; st <= d - 1; ++ st)
	{
		for (int x = 0; x <= 9; ++ x)
		{
			int p = st;
			while(p && s[p + 1] != x) p = nxt[p];
			if(s[p + 1] == x) p ++;
			if(p < d) y.a[st][p] ++;
		}
	}
	x = x * quick(y, c - 1);
	int sum = 0;
	for (int i = 0; i < d; ++ i) sum = (sum + x.a[0][i]) % mod;
	printf("%d", sum);
	return 0;
}

筛质数

欧拉筛:每个合数被它最小的质因数筛去,所以时间复杂度是O(n)的。

1不是质数

如果当前数字是质数,把这个数字加到prime

暴力枚举以前的所有质数,如果当前数字q % now(质数) == 0说明q里面有一个质数会比以后的质数都要小,那么就不能继续计算了,否则q * now就是合数,打上标记就可以了。

抽象

哥德巴赫猜想

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
bool vis[N];
vector<int> pri;
int n;
void prime()
{
	vis[1] = 1;
	for (int i = 2; i <= N - 5; ++ i)
	{
		if(!vis[i]) pri.push_back(i);
		for (int j = 0; j < pri.size(); ++ j)
		{
			if((long long)i * pri[j] > N - 5) break;
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0) break;
		} 
	}
	return ;
}
int main()
{
	prime();
	while(scanf("%d", &n) != EOF)
	{
		if(n == 0) break;
		bool pd = false;
		for (int i = 1; i < pri.size(); ++ i)
		{
			if(pri[i] > n / 2) break;  
			if(!vis[n - pri[i]]) 
			{
				printf("%d = %d + %d\n", n, pri[i], n - pri[i]);
				pd = true;
				break;
			}
		}
		if(!pd) printf("Goldbach's conjecture is wrong.\n");
	} 
	return 0;
}

夏洛克和他的女朋友

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
bool vis[N];
vector<int> pri;
int n;
void prime()
{
	vis[1] = 1;
	for (int i = 2; i <= n + 1; ++ i)
	{
		if(!vis[i]) pri.push_back(i);
		for (int j = 0; j < pri.size(); ++ j)
		{
			if((long long)i * pri[j] > n + 1) break;
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0) break;
		} 
	}
	return ;
}
int main()
{
	scanf("%d", &n);
	prime();
	if(n <= 2)
	{
		printf("1\n");
		for (int i = 1; i <= n; ++ i) printf("1 ");
	}
	else
	{
		printf("2\n");
		for (int i = 2; i <= n + 1; ++ i)
		{
			if(!vis[i]) printf("1 ");
			else printf("2 ");
		}
	 } 
	return 0;
}

约数个数

(* ̄(oo) ̄):1可以作为约数。

约数:若一个数x可以整除y,那么x是y的一个约数。

\(N = P_{1} ^ {c_{1}} + P_{2} ^ {c_{2}} + ... + P_{k} ^ {c_{k}}\)\(N\)的约数个数

\(F(N) = (c_{1} + 1) \times (c_{2} + 1) \times ... \times (c_{k} + 1)\)

\(\sum_{i = 1}^{N} F[i] = \sum_{i = 1}^{N} \frac{N}{i}\)所以总和是一个调和级数,大约为\(O(nlog_{n})\)

关于常数的结论:

int 范围内约数个数最多的数 的 约数个数为1600

轻拍牛头

可以暴力枚举每一个数字,求它的约数是多少,然后直接累加,时间复杂度是\(O(n \sqrt{n})\)

把一个数字分解质因数其实也是非常麻烦的。

我们可以反过来求,每个数字有多少个数是它的倍数。

从1到1000000循环,判断有多少个数字是i的倍数,这个判断不是简单的除法,而是精准到那个倍数是多少,也就是说第二层循环不断跳i。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n, ans[N], cnt[N], a[N], len;
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i)
	{
		scanf("%d", &a[i]);
		cnt[a[i]] ++, len = max(len, a[i]);
	}
	for (int i = 1; i <= len; ++ i)
	{
		for (int j = i; j <= len; j += i) ans[j] += cnt[i];
	}
	for (int i = 1; i <= n; ++ i) printf("%d\n", ans[a[i]] - 1);
	return 0;
}

樱花

拿到题目的第一步可以把公式推一下,看看有没有什么性质。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n, vis[N], cnt[N], ans = 1;
const int mod = 1000000007;
vector<int> pri;
int main()
{
	scanf("%d", &n);
	vis[1] = 1;
	for (int i = 2; i <= n; ++ i)
	{
		if(!vis[i])
		{
			pri.push_back(i);
			for (int j = i; j <= n; j += i)
			{
				int x = j;
				if(j != i) vis[j] = 1;
				while(x && x % i == 0) cnt[i] += 1, x /= i;
			}
		}
	}
	
	for (int i = 0; i < pri.size(); ++ i) ans = (long long)ans * (2 * cnt[pri[i]] + 1) % mod;
	printf("%d", ans);
	return 0; 
}

反素数

要找的节点就是[1, N]中约数个数最多的最小的数字。
N <= 2e9让我们发现:

1.最多只会有9个质因子2

2.同一个质因子的数量小于等于30

3.质因子从小到大排列时,每个质因子的数量一定是不上升的。

那么我们就可以开始愉快的暴力搜索了。

#include <bits/stdc++.h>
using namespace std;
const int inf = 2000000000;
typedef long long ll;
int g = 0, ans = inf;
int n, a[15] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23};
int b[25];
ll quick(ll a, int b)
{
	ll res = 1;
	while(b)
	{
		if(b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}
void dfs(int pos, int pre, int sum, int num)
{
	if(pos == 10)
	{
		if(sum > g) g = sum, ans = num;
		else if(sum == g) ans = min(ans, num);
		return ;
	}
	for (int i = 0; i <= pre; ++ i)
	{
		ll x = (ll)num * quick(a[pos], i);
		if(x > n) break;
		b[pos] = i;
		dfs(pos + 1, i, sum * (i + 1), x);
		b[pos] = 0;
	}
	return ;
}
int main()
{
	scanf("%d", &n);
	dfs(1, inf, 1, 1);
	printf("%d", ans);
	return 0;
}

Hankson的趣味题

真的,想法暴力一点,就是一个暴力枚举

#include <bits/stdc++.h>
using namespace std;
const int N = 45000;//44800
bool vis[N];
int sum, num[15], cnt[15], tot = 0;
vector<int> pri;
void prime()
{
	vis[1] = 1;
	for (int i = 2; i <= 44730; ++ i)
	{
		if(!vis[i]) pri.push_back(i);
		for (int j = 0; j < pri.size() && (long long)pri[j] * i <= 44730; ++ j)
		{
			vis[pri[j] * i] = 1;
			if(i % pri[j] == 0) break;
		}
	}
	return ;
}
int a0, a1, b0, b1;
int ans = 0, f[15][34];
void dfs(int pos, int x)
{
	if(pos == tot + 1)
	{
		if(__gcd(x, a0) == a1 && (long long)x * b0 / __gcd(x, b0) == b1) ans ++; 
		return ;
	}
	for (int i = 0; i <= cnt[pos]; ++ i)
	{
		dfs(pos + 1, x * f[pos][i]);
	}
	return ;
}
int main()
{
	prime();
	int T;
	scanf("%d", &T);
	while(T --)
	{
		scanf("%d %d %d %d", &a0, &a1, &b0, &b1);
		tot = 0;
		int x = b1;
		for (int i = 0; i < pri.size() && (long long)pri[i] * pri[i] <= x; ++ i)
		{
			if(x % pri[i] == 0) 
			{
				num[++ tot] = pri[i], cnt[tot] = 0;
				while(x && x % num[tot] == 0) x = x / num[tot], cnt[tot] ++;
			}
		}
		if(x != 1) num[++ tot] = x, cnt[tot] = 1;
		for (int i = 1; i <= tot; ++ i)
		{
			f[i][0] = 1;
			for (int j = 1; j <= cnt[i]; ++ j) f[i][j] = f[i][j - 1] * num[i];
		}
		ans = 0;
		dfs(1, 1);
		printf("%d\n", ans);
	}
	return 0;
}

欧拉函数

$\varphi(n) = \(比\)n$小的和n互质的数

若n只有一个质因数\(\varphi(n) = P ^k - P^ {k - 1}\)

若a、b互质$\varphi(ab) = \varphi(a) \times \varphi(b) $

\(\varphi(n) = n \times (1 - \frac{1}{P_{1}}) \times ... \times (1 - \frac{1}{P_{k}})\)

可见的点

#include <bits/stdc++.h>
using namespace std;
const int N = 3005;
int n, ph[N], T, sum[N];
bool vis[N];
vector<int> pri;
void get_phi()
{
	vis[1] = 1, ph[1] = 1;
	for (int i = 2; i <= 3000; ++ i)
	{
		if(!vis[i])
		{
			pri.push_back(i);
			ph[i] = i - 1;
		}
		for (int j = 0; j < pri.size() && pri[j] * i <= 3000; ++ j)
		{
			vis[i * pri[j]] = 1, ph[i * pri[j]] = ph[i] * ph[pri[j]];
			if(i % pri[j] == 0)
			{
				ph[i * pri[j]] = ph[i] * pri[j];
				break;
			}
			ph[i * pri[j]] = ph[i] * (pri[j] - 1);
		}
	}
	return ;
}
int main()
{
	get_phi();
	for (int i = 1; i <= 3000; ++ i) sum[i] = sum[i - 1] + ph[i];
	scanf("%d", &T);
	for (int t = 1; t <= T; ++ t)
	{
		scanf("%d", &n);
		printf("%d %d %d\n", t, n, 2 + sum[n] * 2 - 1);
	}
	return 0;
}

最大公约数

#include <bits/stdc++.h>
using namespace std;
const int N = 10000005;
int n, ph[N], sum[N];
typedef long long ll;
ll ans = 0;
bool vis[N];
vector<int> pri;
int main()
{
	scanf("%d", &n);
	vis[1] = 1, ph[1] = 1;
	for (int i = 2; i <= n; ++ i)
	{
		if(!vis[i])
		{
			ph[i] = i - 1; 
			sum[i] = 1;
			pri.push_back(i);
		}
		for (int j = 0; j < pri.size() && (ll)i * pri[j] <= n; ++ j)
		{
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0)
			{
				ph[i * pri[j]] = ph[i] * pri[j];
				break;
			}
			ph[i * pri[j]] = ph[i] * (pri[j] - 1);
		}
		sum[i] += sum[i - 1];
	}
	for (int i = 2; i <= n; ++ i)
	{
		ans = ans + ph[i] * sum[n / i];
	}
	printf("%lld", ans * 2ll + sum[n]);
	return 0;
} 

同余

同余方程

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a, b;
void exgcd(ll a, ll b, ll &x, ll &y)
{
	if(!b) 
	{
		x = 1, y = 0;
		return ;
	}
	exgcd(b, a % b, x, y);
	ll t = x;
	x = y, y = t - y * (a / b);
	return ;
}
int main()
{
	scanf("%lld %lld", &a, &b);
	ll x, y;
	exgcd(a, b, x, y);
	printf("%lld", (x % b + b) % b);
}

青蛙的约会

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll L, x, y, m, n;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
	if(!b)
	{
		x = 1, y = 0;
		return a;
	}
	ll g = exgcd(b, a % b, x, y);
	ll t = x;
	x = y, y = t - y * (a / b);
	return g;
}
int main()
{
	scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &L);
	ll t, p;
	ll g = exgcd(m - n, L, t, p);
	if((y - x) % g != 0) printf("Impossible");
	else
	{
		ll v  = L / g;
		if(v < 0) v = -v;
		t = t * ((y - x) / g);
		t = (t % v + v) % v;
		printf("%lld", t);
	}
}

曹冲养猪

#include <bits/stdc++.h>
using namespace std;
typedef __int128 ll;
ll read()
{
	ll x = 0;
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x;
}
void write(ll x)
{
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
	return ;
}
void exgcd(ll a, ll b, ll &x, ll &y)
{
	if(b == 0)
	{
		x = 1, y = 0;
		return ;
	}
	exgcd(b, a % b, x, y);
	ll t = x;
	x = y, y = t - y * (a / b);
	return ;
}
ll ans = 0, a[15], b[15], M = 1;
int n;
int main()
{
	scanf("%d", &n);
	cout << n << endl;
	for (int i = 1; i <= n; ++ i) 
	{
		a[i] = read(), b[i] = read(), M = M * a[i];
	}
	for (int i = 1; i <= n; ++ i)
	{
		ll Mi = M / a[i];
		ll x, y;
		exgcd(Mi, a[i], x, y);
		ans = (ans + x * Mi % M * b[i] % M) % M;
	}
	write((ans % M + M) % M);
	return 0;
}

组合计数

组合计数是一种方法和一种思想,包含乘法原理、加法原理、组合数、容斥原理、Lucas、Catalan定理等。

牡牛和牝牛(递推法)

\(f[i]\)所有长度是i的,且以1(牧牛)结尾的字符串的数量。
上一个1的位置 <= i - k - 1

注意:\(f[0] = 1\)可以表示前面没有1的一种方案

\(f[i] = \sum_{j = 0}^{i - k - 1}f[j]\)

通过将所有\(f[i]\)累加,暴力枚举最后一个1的位置来计算。

通过记录每次的前缀和来优化。

#include <bits/stdc++.h>
using namespace std;
const int mod = 5000011, N = 100005;
int n, k, dp[N], sum = 0, ans = 0;
int main()
{
	scanf("%d %d", &n, &k);
	dp[0] = 1, sum = 1;
	for (int i = 1; i <= k; ++ i) dp[i] = 1;
	for (int i = k + 1; i <= n; ++ i)
	{
		dp[i] = sum;
		sum = (sum + dp[i - k]) % mod;
	}
	for (int i = 0; i <= n; ++ i) ans = (ans + dp[i]) % mod;
	printf("%d", ans);
	return 0;
}

方程的解(隔板法)

从组合方向考虑解决问题。

\(x^x mod 1000\)可以使用快速幂来求解

\(a_{1} +a_{2} + a_{3} + ...+a_{n} = x^x mod 1000\)

可以用映射的方法就变成了小球和隔板的问题

任何一个方案都可以对应一组a的解。任何一组a的解都可以对应一个放隔板的方案。所以这两个集合是映射关系。

\(ans = C_{n - 1}^{x ^ x mod 1000 - 1}\)

很不幸运的是答案并没有让我们对于某一个数字取模,所以我们需要手写高精度。jflkdsaj

#include <bits/stdc++.h>
using namespace std;
struct data
{
	int a[150];
	void mem() 
	{
		for (int i = 1; i < 150; ++ i) a[i] = 0;
		return ;
	}
	data operator + (const data &g) const 
	{
		data c;
		int t = 0;
		for (int i = 1; i < 150; ++ i)
		{
			t += a[i] + g.a[i];
			c.a[i] = t % 10;
			t /= 10;
		}
		return c;
	}
}f[1000][100];
const int mod = 1000;
int qmi(int a, int b)
{
	int res = 1;
	while(b)
	{
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
int k, x;
int main()
{
	scanf("%d %d", &k, &x);
	x = qmi(x % mod, x);
	for (int i = 0; i < x; ++ i)
	{
		for (int j = 0; j <= i && j < k; ++ j)
		{
			if(!j) f[i][j].mem(), f[i][j].a[1] = 1;
			else f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
		}
	}
	int i = 149;
	while(!f[x - 1][k - 1].a[i]) i --;
	while(i > 0) 
	{
		printf("%d", f[x - 1][k - 1].a[i]);
		i --;
	}
	return 0;
}

车的放置(加法原理)

如果整体做的话可以使用乘法原理,但是这个问题整天做不了。

所以采用分成若干种互不相交的子集的思想,然后相加,对应的就是加法原理。

尝试将这个不规则图形变成两个规则的矩形。

规则矩形[n, m]中放置k个車的方案是C(n, k) * P(m, k)

然后枚举上面和下面分别放置的个数。上半部分放置的位置对下面的影响是固定的。

#include <bits/stdc++.h>
using namespace std;
const int N = 2005, mod = 100003;
int a, b, c, d, fac[N], rev[N], ans = 0, k; 
int C(int x, int y)
{
	if(x < y) return 0;
	return (long long)fac[x] * rev[y] % mod * rev[x - y] % mod;
}
int P(int x, int y)
{
	if(x < y) return 0;
	return (long long)fac[x] * rev[x - y] % mod;
}
int quick(int a, int b)
{
	int res = 1;
	while(b)
	{
		if(b & 1) res = (long long)res * a % mod;
		a = (long long)a * a % mod;
		b >>= 1;
	}
	return res;
}
int main()
{
	scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
	int len = max(max(a + c, b), d);
	if(k > len) printf("0");
	else
	{
		fac[0] = 1;
		for (int i = 1; i <= len; ++ i) fac[i] = fac[i - 1] * i % mod;
		rev[len] = quick(fac[len], mod - 2);
		for (int i = len; i >= 1; -- i) rev[i - 1] = rev[i] * i % mod;
		for (int i = 0; i <= k; ++ i)
		{
			ans = (ans + (long long)C(a, i) * P(b, i) % mod * C(d, k - i) % mod * P(a + c - i, k - i) % mod) % mod;
		}
		printf("%d", ans);
	}
	return 0;
}

数三角形(容斥原理)

求出总共的方案数,然后把不合法的方案数减去

枚举一个直角三角形,直线一定在直角三角形的斜边上。

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n, m;
typedef long long ll;
int gcd(int a, int b)
{
	if(!b) return a;
	return gcd(b ,a % b);
}
ll C(int a)
{
	return (ll)a * (a - 1) * (a - 2) / 6;	
}
int main()
{
	scanf("%d %d", &n, &m);
	n ++, m ++;
	ll res = C(n * m) - C(m) * n - C(n) * m;
	n --, m --;
	for (int i = 1; i <= n; ++ i)
	{
		for (int j = 1; j <= m; ++ j) res = res - 2ll * (gcd(i, j) - 1) * (n - i + 1) * (m - j + 1);
	}
	printf("%lld", res);
	return 0;
}

序列统计(隔板法)

对于这种有序列长度,求序列方案数的,考虑一下隔板法。

可以将题目转化成不定方程的非负整数解问题,然后不等式两边同时加上区间的长度,就变成了不定方程的正整数解问题,就可以使用隔板法了。注意,这里的隔板代表的数字是隔板之前一共有多少个元素。当然,我们要枚举这个序列的长度,也就是不定方程中未知数的个数。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p = 1000003;
int r, l, n;
int quick(int a, int b)
{
	int res = 1;
	while(b)
	{
		if(b & 1) res = (ll)res * a % p;
		a = (ll)a * a % p;
		b >>= 1;
	}
	return res;
}
int C(int a, int b)
{
	int up = 1, down = 1;
	for (int i = a, j = 1; j <= b; ++ j, -- i)
	{
		up = (ll)up * i % p;
		down = (ll)down * j % p; 
	}
	return (ll)up * quick(down, p - 2) % p;
}
int Lucas(int a, int b)
{
	if(a < p && b < p) return C(a, b);
	return (ll)Lucas(a / p, b / p) * C(a % p, b % p) % p;
}
int main()
{
	int T;
	scanf("%d", &T);
	while(T --)
	{
		scanf("%d %d %d", &n, &l, &r);
		printf("%d\n", (Lucas(r - l + n + 1, r - l + 1) + p - 1) % p);
	}
	return 0;
}

容斥原理

Devu和鲜花

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
const int N = 25;
int n, vis[N];
ll a[N], m, ans = 0;
ll quick(ll a, ll b)
{
	ll res = 1;
	while(b)
	{
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	} 
	return res;
}
ll C(ll a, ll b)
{
	if(a == b || b == 0) return 1;
	if(a < b) return 0;
	ll up = 1, down = 1;
	for (ll i = a, j = 1; j <= b; j ++, i --)
	{
		up = up * (i % mod) % mod;
		down = down * (j % mod) % mod;
	}
	return up * quick(down, mod - 2) % mod;
}
void dfs(int x)
{
	if(x == n + 1)
	{
		ll p = m + n;
		int tot = 0;
		for (int i = 1; i <= n; ++ i)
		{
			if(vis[i]) p -= (a[i] + 1), tot ++;
		}
		if(tot & 1) ans = (ans - C(p - 1, n - 1)) % mod;
		else if(tot != 0) ans = (ans + C(p - 1, n - 1)) % mod;
		return ;
	}
	vis[x] = 1;
	dfs(x + 1);
	vis[x] = 0;
	dfs(x + 1);
}
int main()
{
	scanf("%d %lld", &n, &m);
	for (int i = 1; i <= n; ++ i) scanf("%lld", &a[i]);
	dfs(1);
	ans = (ans + C(m + n - 1, n - 1)) % mod;
	printf("%lld", (ans + mod) % mod);
	return 0;
}
posted @ 2025-02-13 10:39  Helioca  阅读(8)  评论(0)    收藏  举报
Document