2021牛客寒假算法基础集训营5 比武招亲(上)(组合数学)
比武招亲(上)
https://ac.nowcoder.com/acm/contest/9985/B
Description
Solution
本人不擅长组合数学,因此只能打表找规律
map<vector<int>, int> mp; void dfs(int now, vector<int> v) { if (now == m) { sort(v.begin(), v.end()); if (mp[v]) { return ; } mp[v]++; ans += v.back() - v[0]; vis[v.back() - v[0]]++; cout << v.back() - v[0] << ' '; return ; } for (int i = 1; i <= n; i++) { v.emplace_back(i); dfs(now + 1, v); v.pop_back(); } } void solve() { vector<int> v; dfs(0, v); for (auto &it : vis) { // 这里可以看出每个数字出现了多少次 cout << it.first << ' ' << it.second << '\n'; } cout << ans << '\n'; }
本题的答案由两个变量 决定,直接找规律有难度
因此不妨用控制变量法,即固定 的值,对 进行变化
不难得到 的时候只会出现 的贡献, 的时候只会出现 的贡献 ....
于是通过改变 的值去看 时 的贡献随 的变化规律, 时 的贡献随 的变化规律
不难得到规律:
的贡献为
的贡献为
的贡献为
对于前面分数的分子可以用前缀积 O(1) 计算, 分母是阶乘, 除法取模的时候乘上阶乘的逆元即可。
因为我是用快速幂求逆元,时间复杂度
再预处理下逆元可以
Code
#include <bits/stdc++.h> typedef long long ll; using namespace std; const int N = 1e6 + 5; const int mod = 998244353; int n, m; int ans = 0; ll qpow(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 M[N], f[N]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int T = 1; //cin >> T; while(T--) { cin >> n >> m; M[1] = m - 1; // 前缀积 for (int i = 2; i <= 5e5; i++) { M[i] = M[i - 1] * (m - 2 + i) % mod; } f[0] = 1; // 阶乘 for (int i = 1; i <= 5e5; i++) { f[i] = f[i - 1] * 1LL * i % mod; } ll res = 0; for (int i = 1; i <= n - 1; i++) { res += 1LL * i * M[i] % mod * qpow(f[i], mod - 2) % mod * (n - i) % mod; res %= mod; } cout << res << '\n'; } return 0; }