昨天大半夜开了一道2000的组合数学,吗的忘记看场次,根本搜不到啥题解,只能对着别人代码一点点琢磨,终于看明白了,搞了套组合数取模的板子,还行
训练赛剩下的题目明天补
文章目录
- CF 1912B Blueprint for Seating
- CF 104555M Maximizing Flight Efficiency
- CF 104555C Challenging Hike
- CF 104555I Investigating Zeroes and Ones
CF 1912B Blueprint for Seating
题目链接
首先是求最小的不方便值,这个很容易,可以分层求,意思是靠近过道的是第一层,与第一层相邻的是第二层,每一层有 2*k
个座位,排座位时是尽量排满小的层,这样很容易求出不方便值最小是多少
比较困难的是共有多少种方式,如果一共排了 x 层,我们只需要考虑第 x 层的排列方式,因为前 x-1 层已经占满了,那第 x 层的排列方式怎么求呢?
我们可以求出第 x 层需要排多少个座位(下文里把它称为p),然后按照中间的座位(中间座位意思是两边都是过道的列, 而不是一边是窗户一边是过道的列)有多少列排两个座位来遍历计算
中间的座位一共有 k-1 列,也就是从 0 循环到 k-1,从 k-1 个中间列里选 i 个排两个座位,剩下的 k+1-i 个里面选 p-2*i 个排一个座位
除法取模需要逆元,总结了组合数的模板在这里
#include <bits/stdc++.h>using namespace std;using i64 = long long;
#define int long long
#define INF 0x3f3f3f3ftypedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;const int N = 11;
const int maxn = 1e6;
const int mod = 998244353;int Jc[maxn];void calJc() //求maxn以内的数的阶乘
{Jc[0] = Jc[1] = 1;for(int i = 2; i < maxn; i++) Jc[i] = Jc[i - 1] * i % mod;
}int pow(int a, int n, int p)
{int ans = 1;while (n){if (n & 1) ans = ans * a % p;a = a * a % p;n >>= 1;}return ans;
}int niYuan(int a, int b) //费马小定理求逆元
{return pow(a, b - 2, b);
}int C(int a, int b)
{if(a < b) return 0;return Jc[a] * niYuan(Jc[b], mod) % mod * niYuan(Jc[a - b], mod) % mod;
}void solve()
{int n, k;cin >> n >> k;if (n <= 2 * k){cout << 0 << ' ';n -= k + 1;if (n == 0) cout << 1 << '\n';else cout << C(k - 1, n) << '\n';return;}int tmp = 2 * k;if (n % tmp == 0){int res = 0;int m = n / tmp;res = (m - 1) * m * k;cout << res << ' ' << 1 << '\n';}else{int res = 0;int m = n / tmp;res = (m - 1) * m * k;n %= tmp;res += m * n;cout << res << ' ';int ans = 0;for (int i = 0; i <= k - 1; i ++ ){if (n < 2 * i) break;int p = n - 2 * i;if (p + i > k + 1) continue;ans = (ans + C(k - 1, i) % mod * C(k + 1 - i, p) % mod) % mod;}cout << ans << '\n';}
}signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);calJc();int t = 1;cin >> t;while (t -- ){solve();}
}
CF 104555M Maximizing Flight Efficiency
题目链接
赛时对floyd的理解不够深+忘判重导致wa了
传统的floyd的循环层是kij,k代表的是利用前k个点,而不是经过k点,这一点需要搞清楚
这一题可以把k放到最里层,含义是经过k这个点,这样可以直接在循环内部判重
(当然也可以像我赛时的ac代码一样先用传统floyd求出每个点之间的非直达最短距离,然后再开一次循环判断
#include <bits/stdc++.h>using namespace std;#define int long longvoid solve()
{int n;cin >> n;vector<vector<int>> g(n + 1, vector<int>(n + 1));for (int i = 1; i <= n; i ++ )for (int j = 1; j <= n; j ++ ) cin >> g[i][j];int ans = 0;for (int i = 1; i <= n; i ++ ){for (int j = i + 1; j <= n; j ++ ){bool flag = false;for (int k = 1; k <= n; k ++ ){if (k == i || k == j) continue;if (g[i][j] > g[i][k] + g[k][j]){cout << -1 << '\n';return;}else if (g[i][j] == g[i][k] + g[k][j] && !flag){ans ++ ;flag = true;}}}}cout << ans << '\n';
}signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int t = 1;while (t -- ){solve();}
}
CF 104555C Challenging Hike
题目链接
树上最长上升子序列的板题
首先是最长上升子序列的log做法,每遍历一个数,用二分判断这个数应该在当前最长上升子序列的哪个位置,更新最大值,把这个方法在dfs里实现,注意,每次函数结束要把修改的位数恢复成原样
#include <bits/stdc++.h>using namespace std;#define int long longvoid solve()
{int n;cin >> n;vector<vector<int>> g(n + 1);for (int i = 2; i <= n; i ++ ){int x; cin >> x;g[i].push_back(x);g[x].push_back(i);}vector<int> v(n + 1);for (int i = 1; i <= n; i ++ ) cin >> v[i];vector<int> lis(n + 1, 0x3f3f3f3f3f3f3f3f);vector<int> ans(n + 1);function<void(int, int)> dfs = [&](int u, int fa){int p = lower_bound(lis.begin() + 1, lis.end(), v[u]) - lis.begin();int tmp = lis[p];lis[p] = v[u];if (fa != -1) ans[u] = max(ans[u], max(p, ans[fa]));else ans[u] = max(ans[u], p);for (int i = 0; i < g[u].size(); i ++ ){int j = g[u][i];if (j == fa) continue;dfs(j, u);}lis[p] = tmp;};dfs(1, -1);for (int i = 2; i <= n; i ++ ) cout << ans[i] << ' ';cout << '\n';
}signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int t = 1;while (t -- ){solve();}
}
CF 104555I Investigating Zeroes and Ones
题目链接
循环看以当前位为结尾的含奇数个1的字串有多少个,加到总答案里
我的想法是,如果前方(包括当前位)已经有奇数个1,那么以当前位结尾、以奇数个1和其之前的0开头的字串一定有奇数个1,我们遍历的时候记录下奇数1和前面的0以及偶数1和前面的0,每遍历一位在答案里加上即可
#include <bits/stdc++.h>using namespace std;#define int long longvoid solve()
{int n;cin >> n;vector<int> b(n);for (int i = 0; i < n; i ++ ) cin >> b[i];int cnt1 = 1, cnt2 = 0; // 奇偶int ans = 0, idx = 0;for (int i = 0; i < n; i ++ ){idx += b[i];if (idx % 2 != 0) ans += cnt1, cnt2 ++ ;else ans += cnt2, cnt1 ++ ;}cout << ans << '\n';
}signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int t = 1;while (t -- ){solve();}
}