counting 太困难了(25.5)

2025/5/5

guomao 限时返厂六六六演都不演了!!!

2025/5/6

5.6 以前的咕咕咕。

P6076

二项式定理与二项式反演的运用

  • \((a+b)^n=\sum\limits_{i=0}^n \dbinom{n}{i}a^i b^{n-i}\)
  • \(f(n)=\sum\limits_{i=0}^n\dbinom{n}{i}g(i),g(n)=\sum\limits_{i=0}^n\dbinom{n}{i}-1^{n-i}f(i)\)

\(f(i)\) 是恰好满足 \(i\) 个条件的方案数,\(g(i)\) 是至多满足 \(i\) 个条件的方案数。

再定义:

\(h(n,m,c)\to \leq n \And \leq m\And \leq c\)
\(p(n,m,c)\to n\geq 1 \And \leq m \And \leq c\)

通过上面的 \(f,g\) 以及 \(h\) 的组合意义可以推出 \(f\) 的表达式。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define inf 0x3fffffff
#define iv inline void
#define il inline int
#define ic inline char
#define int long long

//#define getchar getchar_unlocked
//#define putchar putchar_unlocked

using LL = long long;
using i64 = __int128;
using namespace std;

const int N = 4e6 + 5;
const int mod = 1e9 + 7;

#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)
#define cadd(a, b) (a = ((a + b) % mod + mod) % mod)
#define cdel(a, b) (a = ((a - b) % mod + mod) % mod)

namespace IO {
	const long long MAXR = 100000050;               // 定义缓冲区的最大大小
	char _READ_[MAXR], _PRint_[MAXR];               // 读写缓冲区
	long long _READ_POS_, _PRint_POS_, _READ_LEN_;  // 读缓冲区的位置和长度标记

// 读取单个字符的函数
	ic readc() {
#ifndef ONLINE_JUDGE
		return getchar();  // 本地测试时直接使用 getchar
#endif
		if (!_READ_POS_) {  // 如果当前位置为 0
			if (feof(stdin))
				return -1;                               // 到达文件末尾
			_READ_LEN_ = fread(_READ_, 1, MAXR, stdin);  // 从标准输入读取数据到缓冲区
		}
		char c = _READ_[_READ_POS_++];  // 获取当前字符,并移动位置标记
		if (_READ_POS_ == _READ_LEN_)
			_READ_POS_ = 0;  // 如果到达缓冲区末尾,重置位置标记
		return c;            // 返回读取的字符
	}
// 模板函数,用于读取整型数据
	template <typename T>
	il read(T &x) {
		x = 0;
		register long long flag = 1, c;  // 初始化变量 x 和标志位 flag
		// 跳过所有非数字和负号字符
		while (((c = readc()) < '0' || c > '9') && c != '-')
			if (c < 0)
				return -1;  // 如取到文件末尾
		if (c == '-')
			flag = -1;
		else
			x = c - '0';  // 如果是负号,设置标志位;否则转换为数字
		// 读取剩余的数字字符
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
		x *= flag;  // 正负数处理
		return 0;   // 成功读取
	}
	template <typename T>
	iv write(T x) {
		if(x < 0) putchar('-'), x = -x;
		if (x > 9) write(x / 10);
		putchar(x % 10 + '0');
	}
}  // namespace IO
using namespace IO;

int n, m, k, f[N], g[N], ret;

int qkpow(int a, int b) {
	int ret = 1;
	for(; b; b >>= 1, a = a * a % mod) if(b & 1) ret = ret * a % mod;
	return ret;
}

struct calc {
	int s[N], inv[N];
	void init(int n) {
		s[0] = 1;
		rep(i, 1, n) s[i] = s[i - 1] * i % mod;
		inv[n] = qkpow(s[n], mod - 2);
		pre(i, n, 1) inv[i - 1] = inv[i] * i % mod;
	}
	int C(int a, int b) { return (a < b ? 0 : s[a] * inv[b] % mod * inv[a - b] % mod); }
} c;

signed main() {
	FASTIO;
	
	c.init(N - 5);
	cin >> n >> m >> k;
	
	
	rep(i, 0, k) {
		rep(j, 1, m) cadd(f[i], c.C(m, j) * qkpow(qkpow(i + 1, j) - 1, n) % mod * qkpow(-1, m - j));
//		cout << f[i] << ' ';
	}
	
	int ret = 0;
	
	rep(i, 0, k) cadd(ret, f[i] * c.C(k, i) % mod * qkpow(-1, k - i));
	
	cout << ret;
	
	return 0;
}

P5339

简单题,感觉弱于上面那个题,首先考虑将一组四个人缩为一点,钦定有 \(i\) 组,则一共用 \(n-3\times i\) 个位置,每一个 \(i\) 需要乘一个组合系数 \(\dbinom{n-3\times i}{i}\)

第二步是用剩下的人填满剩下的位置,由于第一步讨论坤坤的人已经考虑了所有位置的方案,所以第二步不讨论坤坤的人不需再考虑位置,直接钦定相对位置算方案即可,方案数是:

\[(n-4i)!\sum[a+b+c+d=n-4i]\dfrac{1}{a!b!c!d!} \]

用 NTT 算。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define inf 0x3fffffff
#define iv inline void
#define il inline int
#define ic inline char
#define int long long

//#define getchar getchar_unlocked
//#define putchar putchar_unlocked

using LL = long long;
using i64 = __int128;
using namespace std;

const int N = 4e6 + 5;
const int mod = 998244353;

#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)
#define cadd(a, b) (a = ((a + b) % mod + mod) % mod)
#define cdel(a, b) (a = ((a - b) % mod + mod) % mod)

namespace IO {
	const long long MAXR = 100000050;               // 定义缓冲区的最大大小
	char _READ_[MAXR], _PRint_[MAXR];               // 读写缓冲区
	long long _READ_POS_, _PRint_POS_, _READ_LEN_;  // 读缓冲区的位置和长度标记

	// 读取单个字符的函数
	ic readc() {
#ifndef ONLINE_JUDGE
		return getchar();  // 本地测试时直接使用 getchar
#endif
		if (!_READ_POS_) {  // 如果当前位置为 0
			if (feof(stdin))
				return -1;                               // 到达文件末尾
			_READ_LEN_ = fread(_READ_, 1, MAXR, stdin);  // 从标准输入读取数据到缓冲区
		}
		char c = _READ_[_READ_POS_++];  // 获取当前字符,并移动位置标记
		if (_READ_POS_ == _READ_LEN_)
			_READ_POS_ = 0;  // 如果到达缓冲区末尾,重置位置标记
		return c;            // 返回读取的字符
	}
	// 模板函数,用于读取整型数据
	template <typename T>
	il read(T &x) {
		x = 0;
		register long long flag = 1, c;  // 初始化变量 x 和标志位 flag
		// 跳过所有非数字和负号字符
		while (((c = readc()) < '0' || c > '9') && c != '-')
			if (c < 0)
				return -1;  // 如取到文件末尾
		if (c == '-')
			flag = -1;
		else
			x = c - '0';  // 如果是负号,设置标志位;否则转换为数字
		// 读取剩余的数字字符
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
		x *= flag;  // 正负数处理
		return 0;   // 成功读取
	}
	template <typename T>
	iv write(T x) {
		if(x < 0) putchar('-'), x = -x;
		if (x > 9) write(x / 10);
		putchar(x % 10 + '0');
	}
}  // namespace IO
using namespace IO;

int n, m, k, A, B, C, D, f[N], g[N], rev[N], bit, a[N], b[N], c[N], d[N], tot;

int qkpow(int a, int b) {
	int ret = 1;
	for(; b; b >>= 1, a = a * a % mod) if(b & 1) ret = ret * a % mod;
	return ret;
}



struct calc {
	int s[N], inv[N];
	void init(int n) {
		s[0] = 1;
		rep(i, 1, n) s[i] = s[i - 1] * i % mod;
		inv[n] = qkpow(s[n], mod - 2);
		pre(i, n, 1) inv[i - 1] = inv[i] * i % mod;
	}
	int C(int a, int b) {
		return (a < b ? 0 : s[a] * inv[b] % mod * inv[a - b] % mod);
	}
} cc;

void Init_NTT(int n) {
	bit = 0;
	while((1 << bit) < n << 1) ++bit;
	tot = 1 << bit;
	rep(i, 0, tot) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit - 1);
}

void NTT(int a[], int inv = 1) {
	int n = tot;
	rep(i, 1, n) if(i < rev[i]) swap(a[i], a[rev[i]]);
	for(int mid = 2; mid <= n; mid <<= 1LL) {
		int k = mid >> 1,
		    gn = (qkpow(inv == 1 ? 3 : qkpow(3, mod - 2), (mod - 1) / mid));
		for(int i = 0; i < n; i += mid) {
			int g = 1;
			for(int j = 0; j < k; ++j, g = g * gn % mod) {
				int x = a[i + j], y = g * a[i + j + k] % mod;
				a[i + j] = (x + y) % mod, a[i + j + k] = (x - y + mod) % mod;
			}
		}
	}
	if(inv == -1) {
		rep(i, 0, n) a[i] = a[i] * qkpow(n, mod - 2) % mod;
	}
}

/*
part2
求在剩下的不讨论坤坤的人中填满 n - 4 * i 个位置的方案数

(n - 4 * i)! \sum (a + b + c + d = n - 4 * i) (n - 4 * i)!
											---------------
											a! * b! * c! * d!
*/

int calc(int x, int p) {//f[x]:有 x 人不讨论坤坤的方案数
	if(x > m - p * 4) return 0;
	Init_NTT(m - p * 4);
	rep(i, 0, tot - 1) a[i] = (i <= A - p ? cc.inv[i] : 0), b[i] = (i <= B - p ? cc.inv[i] : 0), c[i] = (i <= C - p ? cc.inv[i] : 0), d[i] = (i <= D - p ? cc.inv[i] : 0);
	NTT(a), NTT(b), NTT(c), NTT(d);
	rep(i, 0, tot - 1) a[i] = a[i] * b[i] % mod * c[i] % mod * d[i] % mod;
	NTT(a, -1);
	//	cout << x << ':' << a[x] << '\n';
	return a[x] * cc.s[x] % mod;
}

signed main() {
	FASTIO;

	cc.init(N - 5);
	cin >> n >> A >> B >> C >> D, k = min({A, B, C, D, n / 4}), m = A + B + C + D;
	rep(i, 0, k) f[n - 4 * i] = calc(n - 4 * i, i);

	int ret = 0;
	rep(i, 0, k) cadd(ret, qkpow(-1, i) * cc.C(n - 3 * i, i) % mod * f[n - 4 * i] % mod);
	cout << ret;

	return 0;
}

2025/5/8

ABC279G

\(f_i\) 表示长度为 \(i\) 的网格的个数。

分两种情况,第 \(i\) 格和前面 \(k-1\) 格颜色都不一样,这样随便填,如果有一样的,那么就只能填前 \(k-1\) 格元素的两种颜色,\(k+1\sim n\) 的转移很简单。

考虑怎么对前 \(k\) 个赋初值,前 \(k\) 个相当于是在 \(c\) 种颜色里选两种填,但是这样会算重,只填一种颜色会被多算,那么有:

\[f_i=\dbinom{c}{2}\times 2^i - c\times (c-2) \]

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define inf 0x3fffffff
#define iv inline void
#define il inline int
#define ic inline char
#define int long long

//#define getchar getchar_unlocked
//#define putchar putchar_unlocked

using LL = long long;
using i64 = __int128;
using namespace std;

const int N = 1e6 + 5;
const int mod = 998244353;

#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)
#define cadd(a, b) (a = ((a + b) % mod + mod) % mod)
#define cdel(a, b) (a = ((a - b) % mod + mod) % mod)

namespace IO {
	const long long MAXR = 100000050;               // 定义缓冲区的最大大小
	char _READ_[MAXR], _PRint_[MAXR];               // 读写缓冲区
	long long _READ_POS_, _PRint_POS_, _READ_LEN_;  // 读缓冲区的位置和长度标记

	// 读取单个字符的函数
	ic readc() {
#ifndef ONLINE_JUDGE
		return getchar();  // 本地测试时直接使用 getchar
#endif
		if (!_READ_POS_) {  // 如果当前位置为 0
			if (feof(stdin))
				return -1;                               // 到达文件末尾
			_READ_LEN_ = fread(_READ_, 1, MAXR, stdin);  // 从标准输入读取数据到缓冲区
		}
		char c = _READ_[_READ_POS_++];  // 获取当前字符,并移动位置标记
		if (_READ_POS_ == _READ_LEN_)
			_READ_POS_ = 0;  // 如果到达缓冲区末尾,重置位置标记
		return c;            // 返回读取的字符
	}
	// 模板函数,用于读取整型数据
	template <typename T>
	il read(T &x) {
		x = 0;
		register long long flag = 1, c;  // 初始化变量 x 和标志位 flag
		// 跳过所有非数字和负号字符
		while (((c = readc()) < '0' || c > '9') && c != '-')
			if (c < 0)
				return -1;  // 如取到文件末尾
		if (c == '-')
			flag = -1;
		else
			x = c - '0';  // 如果是负号,设置标志位;否则转换为数字
		// 读取剩余的数字字符
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
		x *= flag;  // 正负数处理
		return 0;   // 成功读取
	}
	template <typename T>
	iv write(T x) {
		if(x < 0) putchar('-'), x = -x;
		if (x > 9) write(x / 10);
		putchar(x % 10 + '0');
	}
}  // namespace IO
using namespace IO;

int n, m, f[N], k, c;

int qkpow(int a, int b) {
	int ret = 1;
	for(; b; b >>= 1, a = a * a % mod) if(b & 1) ret = ret * a % mod;
	return ret;
}

struct calc {
    int s[N], inv[N];
    void init(int n) {
        s[0] = 1;
        rep(i, 1, n) s[i] = s[i - 1] * i % mod;
        inv[n] = qkpow(s[n], mod - 2);
        pre(i, n, 1) inv[i - 1] = inv[i] * i % mod;
    }
    int C(int a, int b) { return (a < b ? 0 : s[a] * inv[b] % mod * inv[a - b] % mod); }
} cc;


signed main() {
	FASTIO;

	cc.init(N - 5);
	cin >> n >> k >> c;
	rep(i, 1, k) f[i] = ((c * (c - 1) % mod * qkpow(2, mod - 2) % mod * qkpow(2, i) % mod - c * (c - 2)) % mod + mod) % mod;
	rep(i, k + 1, n) f[i] = f[i - 1] * 2 % mod + f[i - k + 1] * (c - 2) % mod, f[i] %= mod;

	cout << f[n];

	return 0;
}
posted @ 2025-05-06 21:41  Iron_Spade  阅读(22)  评论(0)    收藏  举报