当前位置: 首页 > news >正文

20250823

组合计数专题,选记一些。

T4

水の三角

枚举走了几个斜的,剩下就是基础反射容斥了。

代码
#include <iostream>
#define int long long
using namespace std;
const int P = 998244353;
int qpow(int x, int y) {int ret = 1;while (y) {if (y & 1) ret = ret * x % P;y >>= 1;x = x * x % P;}return ret;
}
int fac[2000005], ifac[2000005], inv[2000005];
void Cpre(int n) {fac[0] = fac[1] = ifac[0] = ifac[1] = inv[0] = inv[1] = 1;for (int i = 2; i <= n; i++) {fac[i] = fac[i - 1] * i % P;inv[i] = (P - P / i) * inv[P % i] % P;ifac[i] = ifac[i - 1] * inv[i] % P;}
}
inline int C(int n, int m) { return n < 0 || m < 0 || n < m ? 0 : fac[n] * ifac[m] % P * ifac[n - m] % P; }
signed main() {Cpre(2000000);int tc;cin >> tc;while (tc--) {int x, n = 1, m;cin >> x;int l = 1, r = 1000000, mid;while (l <= r) {mid = (l + r) >> 1;if (mid * (mid + 1) / 2 >= x) n = mid, r = mid - 1;else l = mid + 1;}m = x - n * (n - 1) / 2;--n, --m;int ans = 0;for (int i = 0; i <= m; i++) ans += (C(n + m - i * 2, n - i) + P - C(n + m - i * 2, m - i - 1)) * C(n + m - i, i) % P;cout << ans % P << "\n";}return 0;
}

T5

残暴圣所

对每个数对算贡献。会发现下标差一样的数对贡献系数都相同,于是只需要一遍差卷积求出差为 \(i\) 的数对的乘积之和,剩下的卡特兰数一下即可。

代码
#include <iostream>
#include <vector>
#define int long long
using namespace std;
const int P = 998244353;
const int g = 3, gi = 332748118;
int fac[2000005], ifac[2000005], inv[2000005];
void Cpre(int n) {fac[0] = fac[1] = ifac[0] = ifac[1] = inv[0] = inv[1] = 1;for (int i = 2; i <= n; i++) {fac[i] = fac[i - 1] * i % P;inv[i] = (P - P / i) * inv[P % i] % P;ifac[i] = ifac[i - 1] * inv[i] % P;}
}
inline int C(int n, int m) { return n < 0 || m < 0 || n < m ? 0 : fac[n] * ifac[m] % P * ifac[n - m] % P; }
using poly = vector<int>;
int qpow(int x, int y = P - 2) {int ret = 1;while (y) {if (y & 1) ret = ret * x % P;y >>= 1;x = x * x % P;}return ret;
}
int r[4000005];
void NTT(vector<int> &f, int opt = 1) {int n = f.size(), m = 1; while (m < n) m <<= 1;f.resize(n = m);for (int i = 1; i < n; i++) {r[i] = (r[i >> 1] >> 1) | ((i & 1) ? (n >> 1) : 0);r[i] < i ? swap(f[r[i]], f[i]) : void();}for (int i = 1; i < n; i <<= 1) {int w = qpow(opt != 1 ? gi : g, (P - 1) / (i << 1));for (int j = 0; j < n; j += (i << 1)) {for (int k = 0, t = 1; k < i; k++, t = t * w % P) {int x = f[j | k], y = t * f[i | j | k] % P;f[j | k] = (x + y) % P;f[i | j | k] = (x - y + P) % P;}}}if (opt != 1) {int x = qpow(n);for (int i = 0; i < n; i++) f[i] = f[i] * x % P;}
}
int Cat(int n) { return (C(n * 2, n) - C(n * 2, n - 1) + P) % P; }
poly F, G;
int n;
int a[1000005];
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);Cpre(2000000);cin >> n;for (int i = 1; i <= n * 2; i++) cin >> a[i];F.resize(n << 2 | 1), G.resize(n << 2 | 1);for (int i = 1; i <= n * 2; i++) F[i] = a[i], G[i] = a[n * 2 - i + 1];NTT(F), NTT(G);for (int i = 0; i < (int)F.size(); i++) F[i] = F[i] * G[i] % P;NTT(F, -1);int ans = 0;for (int i = 1; i <= n * 2; i += 2) {int x = F[n * 2 + 1 + i];ans += x * Cat((n * 2 - i - 1) / 2) % P * Cat((i - 1) >> 1) % P;}cout << ans % P << "\n";return 0;
}

T6

过河卒二

从不定格以不定方向走出 \(n \times m\) 网格图右上边界 \(\longrightarrow\) 走到 \((n + 1, m + 1)\)

剩下就是基础 dp 和容斥了,也是要枚举走了几个斜的。

代码
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int P = 59393;
int fac[60005], ifac[60005], inv[60005];
void Cpre(int n) {fac[0] = fac[1] = ifac[0] = ifac[1] = inv[0] = inv[1] = 1;for (int i = 2; i <= n; i++) {fac[i] = fac[i - 1] * i % P;inv[i] = (P - P / i) * inv[P % i] % P;ifac[i] = ifac[i - 1] * inv[i] % P;}
}
inline int _C(int n, int m) { return n < 0 || m < 0 || n < m ? 0 : fac[n] * ifac[m] % P * ifac[n - m] % P; }
inline int C(int n, int m) { return n < 0 || m < 0 || n < m ? 0 : (n == 0 ? (m == 0) : C(n / P, m / P) * _C(n % P, m % P) % P); }
int n, m, K;
pair<int, int> p[25];
int calc(int x, int y) {int ret = 0;for (int k = 0; k <= min(x, y); k++) ret += C(x + y - k, k) * C(x + y - k * 2, x - k) % P;return ret;
}
int f[25];
signed main() {Cpre(P - 1);cin >> n >> m >> K;for (int i = 1; i <= K; i++) cin >> p[i].first >> p[i].second;sort(p + 1, p + K + 1);int ans = calc(n, m);for (int i = 1; i <= K; i++) {int x = p[i].first, y = p[i].second;f[i] = calc(x - 1, y - 1);for (int j = 1; j < i; j++) {if (p[j].first <= x && p[j].second <= y) f[i] += P - f[j] * calc(x - p[j].first, y - p[j].second) % P;}f[i] %= P;ans += P - f[i] * calc(n + 1 - x, m + 1 - y) % P;}cout << ans % P << "\n";return 0;
}

T7

看电影

考虑数合法方案。在一个合法方案中,每个人都能够一直走下去,直到一个空座位。因此我们把这样美好的希望放回序列,发现只需要把序列连成一个环就可以让每个人一直走下去之后能遇到一个空座位。但是这个时候我们没法判断一个方案到底真合法还是走过了 \(k \rightarrow 1\) 边,于是在 \(k\) 后面摆一个 \(k + 1\) 座位用来钓鱼,这样如果 \(k + 1\) 座位有人坐了我们就知道这其实不是一个合法方案。由于这是一个环,我们的 \(k + 1\) 是可以随便乱移的。显然合法的位置只有 \(k + 1 - n\) 种,因为所有人落座之后只有这么多位置是空的。因为我们要数的是合法方案数,因此 \(k + 1\) 只能放在空位。然后这个时候所有人都变成在 \(k + 1\) 个里面选一个。注意在确定了 \(k + 1\) 的位置之后,我们永远可以把此时的一个选座位的方案映射回值域为 \([1, k]\) 的选座位方案。由于这是一个环,因此旋转对称,总方案要除以环长。也就是合法总方案为 \((k + 1)^{n - 1}(k + 1 - n)\),高精度一下即可。

代码
#include <iostream>
using namespace std;
const int MXDigit = 1000;
class high {public:short num[MXDigit];bool positive;int digits;high () {for (int i = 0; i < MXDigit; i++) {num[i] = 0;}positive = true;digits = 0;}inline short & operator[](int index){return num[MXDigit - index - 1];}inline void operator=(int inte) {digits = 0;int n = MXDigit - 1;positive = (inte >= 0);inte *= (((int)positive - 1 << 1) + 1);while (inte) {num[n] = inte % 10;inte /= 10;n--;digits++;}}inline void getdigits() {digits = 0;for (; num[digits] == 0; digits++);digits = MXDigit - digits;}
} unit;
ostream & operator<<(ostream & out, high ha);
inline void hswap(high & ha, high & hb) {high hc;hc = hb;hb = ha;ha = hc;
}
high operator-(high ha, high hb);
bool operator>(high ha, high hb) {if (ha.digits > hb.digits) return true;for (int i = 0; i < MXDigit; i++) {if (ha.num[i] == hb.num[i]) {continue;}return ha.num[i] > hb.num[i];}return false;
}
bool operator<(high ha, high hb) {if (ha.digits < hb.digits) return true;for (int i = 0; i < MXDigit; i++) {if (ha.num[i] == hb.num[i]) {continue;}return ha.num[i] < hb.num[i];}return false;
}
bool operator==(high ha, high hb) {if (ha.digits != hb.digits) return false;ha.getdigits();for (int i = ha.digits - 1; i >= 0; i++) {if (ha[i] != hb[i]) return false;}return (ha.positive == hb.positive);
}
inline bool operator>=(high ha, high hb) { return (!(ha < hb)); }
inline bool operator<=(high ha, high hb) { return (!(ha > hb)); }
high High(int x) {int cnt = 0;high ret;ret.positive = (x >= 0);x *= ((ret.positive - 1 << 1) + 1);while (x) {ret[cnt] = (x > 0 ? x % 10 : -x % 10);cnt++, x /= 10;ret.digits++;}return ret;
}
inline high operator<<(high ha, int ii) {ha.getdigits();for (int i = ha.digits - 1; i >= 0; i--) {i + ii >= MXDigit ? 0 : ha[i + ii] = ha[i], ha[i] = 0;}ha.digits += ii;return ha;
}
inline void operator<<=(high & ha, int ii) { ha = ha << ii; }
inline high operator>>(high ha, int ii) {ha.getdigits();for (int i = 0; i < ha.digits; i++) {i + ii >= MXDigit ? 0 : ha[i] = ha[i + ii], ha[i + ii] = 0;}return ha;
}
inline void operator>>=(high & ha, int ii) { ha = ha >> ii; }
inline high operator-(high ha) {ha.positive = !ha.positive;return ha;
}
high operator+(high ha, high hb) {ha.getdigits(), hb.getdigits();if (ha.positive < hb.positive) {hswap(ha, hb);return ha - hb;} else if (ha.positive > hb.positive) {return ha - hb;}high hc;for (int i = 0; i < max(ha.digits, hb.digits); i++) {hc[i] += (ha[i] + hb[i]);hc[i + 1] += hc[i] / 10;hc[i] %= 10;}hc.getdigits();hc.positive = ha.positive;return hc;
}
inline high operator+(high h, int ii) { return h + High(ii); }
inline high operator+(int ii, high h) { return h + High(ii); }
inline void operator+=(high & ha, high hb) { ha = ha + hb; }
inline void operator+=(high & ha, int ii) { ha = ha + ii; }
high operator-(high ha, high hb) {ha.getdigits();if (ha.positive != hb.positive) {return ha + (-hb);}high hr;if (ha < hb) {hswap(ha, hb);hr.positive = false;}for (int i = 0; i < ha.digits; i++) {if (ha[i] < hb[i]) {ha[i + 1]--;hr[i] += 10;}hr[i] = hr[i] + ha[i] - hb[i];}hr.getdigits();hr.positive = (ha.positive == hr.positive);return hr;
}
inline high operator-(high h, int ii) { return h - High(ii); }
inline high operator-(int ii, high h) { return High(ii) - h; }
inline void operator-=(high & ha, high hb) { ha = ha - hb; }
inline void operator-=(high & ha, int ii) { ha = ha - ii; }
high operator*(high ha, high hb) {high hc;ha.getdigits(), hb.getdigits();for (int i = 0; i < ha.digits; i++) {for (int j = 0; j < hb.digits && i + j + 1 < MXDigit; j++) {hc[i + j] += ha[i] * hb[j];hc[i + j + 1] += hc[i + j] / 10;hc[i + j] %= 10;}}hc.getdigits();hc.positive = !(ha.positive ^ hb.positive);return hc;
}
inline high operator*(high h, int ii) { return h * High(ii); }
inline high operator*(int ii, high h) { return h * High(ii); }
inline void operator*=(high & ha, high hb) { ha = ha * hb; }
inline void operator*=(high & ha, int ii) { ha = ha * ii; }
high operator/(high ha, high hb) {ha.getdigits(), hb.getdigits();high hc, mv, tri;int mvs;while (ha >= hb) {mv = hb;mvs = 0;if (ha.digits - hb.digits > 1) {mv = hb << (ha.digits - hb.digits - 1);mvs = ha.digits - hb.digits - 1;}while ((mv << 1) <= ha) {mv <<= 1;mvs++;}hc[mvs] = 1;tri = mv;while (tri + mv <= ha) {tri += mv;hc[mvs]++;}ha -= tri;}hc.getdigits();return hc;
}
inline high operator/(high h, int ii) { return h / High(ii); }
inline high operator/(int ii, high h) { return High(ii) / h; }
inline void operator/=(high & ha, high hb) { ha = ha / hb; }
inline void operator/=(high & ha, int ii) { ha = ha / ii; }
high operator%(high ha, high hb) {high ret = ha - hb * (ha / hb);return ret;
}
inline high operator%(high h, int ii) { return h % High(ii); }
inline high operator%(int ii, high h) { return High(ii) % h; }
inline void operator%=(high & ha, high hb) { ha = ha % hb; }
inline void operator%=(high & ha, int ii) { ha = ha % ii; }
high operator^(high ha, int ind) {high ret;ret = 1;while (ind) {if(ind & 1) ret = ret * ha;ind >>= 1;ha = ha * ha;}ha.getdigits();return ret;
}
ostream & operator<<(ostream & out, high ha) {bool flag = false;for (int i = 0; i < MXDigit; i++) {flag |= (ha[i] != 0);}if (!flag) {out << 0;return out;}flag = false;out << (ha.positive ? "" : "-");for (int i = 0; i < MXDigit; i++) {if (!flag) {flag = (ha.num[i] != 0);}if (flag) {out << ha.num[i];}}return out;
}
int operator<<(int& x, high ha) {bool flag = false;for (int i = 0; i < MXDigit; i++) {flag |= (ha[i] != 0);}if (!flag) {return x = 0;}flag = false;x = 0;for (int i = 0; i < MXDigit; i++) {if (!flag) {flag = (ha.num[i] != 0);}if (flag) {x = x * 10 + ha.num[i];}}x *= ((ha.positive) ? 1 : -1);return x;
}
istream & operator>>(istream & in, high & ha){string str;in >> str;int sise = str.size();if (str[0] == '-') {ha.positive = 0;}for (int i = (str[0] == '-'); i < sise; i++) {ha.num[MXDigit - sise + i] = str[i] - '0';}return in;
}
int gcd(int a, int b) { return (b ? gcd(b, a % b) : a); }
// (k + 1) ^ (n - 1) * (k + 1 - n) / (k ^ n)
int main() {unit = 1;int tc;cin >> tc;while (tc--) {high n, k;cin >> n >> k;if (n > k) {cout << "0 1\n";continue;}int N;N << n;high a = ((k + 1) ^ (N - 1));high x = k + 1 - n, y = k ^ N, z = y % x;int b, c;b << (k + 1 - n), c << z;int t = gcd(b, c);x /= t, y /= t;cout << a * x << " " << y << "\n";}return 0;
}

T10

Max Limited Sequence

上界限制相同的拉出来,dp 即可。显然不同的上界限制间独立。

代码
#include <iostream>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;
const int P = 998244353;
int qpow(int x, int y = P - 2) {int ret = 1;while (y) {if (y & 1) ret = ret * x % P;y >>= 1;x = x * x % P;}return ret;
}
int n, m, q;
struct X {int l, r, x;
} a[200005];
int dsu[200005], key[200005], kcnt;
int getf(int x) { return (dsu[x] == x ? x : (dsu[x] = getf(dsu[x]))); }
vector<int> vec[200005];
int f[200005];
signed main() {cin >> n >> m >> q;for (int i = 1; i <= q; i++) cin >> a[i].l >> a[i].r >> a[i].x;sort(a + 1, a + q + 1, [](X x, X y) { return x.x < y.x; });for (int i = 1; i <= n + 1; i++) dsu[i] = i;int ans = 1;for (int i = 1, j = 1; i <= q; i = j) {kcnt = 0;while (j <= q && a[j].x == a[i].x) ++j;for (int k = i; k < j; k++) {for (int x = getf(a[k].l); x <= a[k].r; x = getf(x)) key[++kcnt] = x, dsu[x] = x + 1;}sort(key + 1, key + kcnt + 1);for (int k = 0; k <= kcnt + 1; k++) f[k] = 0, vec[k].clear();for (int k = i; k < j; k++) vec[upper_bound(key + 1, key + kcnt + 1, a[k].r) - key - 1].emplace_back(lower_bound(key + 1, key + kcnt + 1, a[k].l) - key);int lim = 0, val = qpow(a[i].x);f[0] = qpow(a[i].x, kcnt + 1);for (int k = 1; k <= kcnt + 1; k++) {for (auto v : vec[k - 1]) lim = max(lim, v);f[k] = (f[k - 1] + (lim ? P - f[lim - 1] : 0)) * val % P;f[k] = (f[k - 1] + f[k]) % P;}ans = ans * (f[kcnt + 1] + P - f[kcnt]) % P;}for (int i = getf(1); i <= n; i = getf(i)) ans = ans * (m + 1) % P, dsu[i] = i + 1;cout << ans << "\n";return 0;
}

T11

数好图

朴素的 dp,我们尝试记录当前考虑了几个点,有多少点从 \(1\) 可达,能到 \(n\);从 \(1\) 不可达,能到 \(n\),从 \(1\) 可达,到不了 \(n\);以及两个都不能。这状态数爆了,需要压缩。会发现所有第三种点走到的点都是第三种点,所有能走到第二、四种点的都是第二、四种点。于是发现这三个独立了,我们分别称为第一、二(第三种点)、三(第二、四种点)类点。我们只需要先算出第一类点的个数,然后依次往里加入第二、三类点即可。第一类点的个数,我们考虑一张所有点都能从 \(1\) 到且能到 \(n\) 的 dag,其充要条件是每个点都有入度和出度。我们对着出度容斥,\(f_{i, j}\) 表示前 \(i\) 个点,钦定 \(j\) 个没有出度的方案数。转移时只需要保证每个新点都有入度。这样就算出来第一类点的个数。然后再做两遍 dp 分别加入后两种点即可。每次加入时考虑向之前已经加入的所有点的连边。总时间复杂度 \(\mathcal{O}(n^2)\)

代码
#include <iostream>
#include <string.h>
#define int long long
using namespace std;
int n, P;
inline void Madd(int &x, int y) { (x += y) >= P ? (x -= P) : 0; }
int qpow(int x, int y) {int ret = 1;while (y) {if (y & 1) ret = ret * x % P;y >>= 1;x = x * x % P;}return ret;
}
int C[2005][2005];
int pw[2005];
int f[2005][2005], g[2005][2005], g1[2005], ans[2005];
signed main() {cin >> n >> P;for (int i = C[0][0] = pw[0] = 1; i <= 2000; i++) {pw[i] = (pw[i - 1] * 2) % P;for (int j = C[i][0] = 1; j <= i; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;}f[1][0] = 1;for (int i = 1; i <= n; i++) {for (int j = 0; j < i; j++) {Madd(f[i + 1][j], f[i][j] * (pw[i - j] - 1) % P);Madd(f[i + 1][j + 1], f[i][j] * (pw[i - j - 1] - 1) % P);Madd(g1[i], (j & 1) ? P - f[i][j] : f[i][j]);}}memset(f, 0, sizeof f);f[1][1] = 1;for (int i = 1; i <= n; i++) {for (int j = 1; j <= i; j++) {Madd(f[i + 1][j], f[i][j] * (pw[i] - 1) % P);Madd(f[i + 1][j + 1], f[i][j]);}}g[1][0] = 1;for (int i = 1; i <= n; i++) {for (int j = 0; j < i; j++) {Madd(g[i + 1][j], g[i][j]);Madd(g[i + 1][j + 1], g[i][j] * pw[i] % P);}}for (int i = 2; i <= n; i++) {for (int j = 0; j <= n - i; j++) ans[i] += f[n - j - 1][i - 1] * g1[i] % P * g[n - 1][j] % P;ans[i] %= P;}ans[0] = ans[2];for (int i = 0; i <= n; i++) cout << ans[i] << " ";cout << "\n";return 0;
}
http://www.sczhlp.com/news/38407/

相关文章:

  • 做盗版频网站环球网
  • 高级网站开发工信部网络广告策划书范文
  • 网站后台查找软件软件开发公司
  • 400网站建设电话老王搜索引擎入口
  • 网站开发最新架构外贸建站平台
  • 哪建网站好适合发表个人文章的平台
  • 网站运营工作具体做啥热门推广平台
  • mip网站设计科学新概念seo外链
  • 湛江优化网站排名自己做网络推广怎么做
  • 现代 C++ 并发与多线程编程全景解析
  • 哪些企业需要做网站宁波seo在线优化公司
  • 做什么网站赚钱最快seo监控
  • 吉林省建设厅网站首页重庆seo技术分享
  • 网站域名com和cn的差别在哪里营销网
  • wordpress 站内消息网络项目怎么推广
  • Yjs数据模型分析
  • Windows下OpenOCD使用Jlink进行下载
  • 做网站技巧seo营销推广多少钱
  • 做网站推广用自己维护吗手机优化
  • 外贸网站建设商家英文谷歌seo
  • 做网站的软件网络推广招聘
  • 做一个在线支付网站百度快速seo优化
  • 扫二维码进入个人的购物网站如何做滕州今日头条新闻
  • 越秀企业网站建设亿驱动力竞价托管
  • 青海网站建设有哪些学电脑培训班多少一个月
  • web技术的网站开发在广州做seo找哪家公司
  • id怎么转wordpress河北seo基础教程
  • 1024cctvcom戊人影祝九幺seo工具
  • 大连网站设计九首选仟亿科技百度关键词排名手机
  • 工业园网站建设站长工具查询