题意: 给出一个多边形的坐标和圆的半径, 多边形可以在圆内滚动, 问点 1 在成为转动中心到下一次成为转动中心的过程中经过的路程长度.
思路: 枚举点 2 - n 成为转动中心的情况下点 1 的路程. 没有系统训练过计算几何, 也许代码有待改进.
view code
#include <bits/stdc++.h> using namespace std; #define ll long long #define inc(i, l, r) for (int i = l; i <= r; i++) const int maxn = 1e2 + 5; int n, r; double x[maxn], y[maxn], res; double dis(int i, int j) { return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])); } double aatan(int i, int j) { if (x[i] == x[j]) { if (y[i] > y[j]) return M_PI / 2; return 3 * M_PI / 2; } return atan((y[i] - y[j]) / (x[i] - x[j])); } double alp(int i) { double r = aatan(i, i - 1) - aatan(i, i + 1); while (r < 0) r += M_PI; while (r > M_PI) r -= M_PI; return r; } int main() { cin >> n >> r; inc(i, 1, n) cin >> x[i] >> y[i]; x[n + 1] = x[1], y[n + 1] = y[1]; inc(i, 1, n - 1) { double d = dis(i, i + 1), d2 = dis(i + 1, i + 2), p = dis(1, i + 1); double a = asin(d / 2 / r), a2 = asin(d2 / 2 / r); res += (M_PI - a - a2 - alp(i + 1)) * p; } printf("%.12f\n", res); }
题意: 给出一个长度为 n 的字符串. 有 q 组操作, 一种是选择长度为 m 的区间 \([x_i, x_i + m - 1]\) 翻转, 一种是查询串上某一位置的字符. 每次翻转操作的起始坐标 \(x_i\) 是严格不减的. n ≤ 1e6, q ≤ 1e6.
思路: 根据操作坐标的单调性, 使用滑动窗口维护被翻转的区间. 滑动窗口是一段长度为 m 的区间, 用 std::deque 保存内部元素, 它会从左往右扫描一遍区间, 遇到翻转操作时, 打上翻转标记(或取消翻转标记)而不实际改变内部元素. 处于翻转状态时, 移动滑动窗口, 本来要舍弃队首元素, 但实际上该元素在队尾, 所以执行 pop_front()
; 本来要往队尾加入元素, 但该区间是翻转状态, 所以应加入队首以正确应对查询. 查询时考虑当前滑动窗口的位置与翻转标记即可. 时间复杂度 O(n).
view code
#include <bits/stdc++.h> using namespace std; #define ll long long #define inc(i, l, r) for (int i = l; i <= r; i++) const int maxn = 1e6 + 5; int n, m, q, x, y, f, top; char a[maxn]; deque<char> deq; int main() { scanf("%d %d %d", &n, &m, &q); scanf("%s", a + 1); inc(i, 1, m) deq.push_back(a[i]); top = 1; while (q--) { scanf("%d %d", &x, &y); if (x == 1) { while (top < y) { if (f) { a[top] = deq.back(); deq.pop_back(); deq.push_front(a[top + m]); } else { a[top] = deq.front(); deq.pop_front(); deq.push_back(a[top + m]); } top++; } f = 1 - f; } else { if (y >= top && y < top + m) { if (f) printf("%c", deq[m - 1 - (y - top)]); else printf("%c", deq[y - top]); } else printf("%c", a[y]); } } printf("\n"); }
题意: A 国人与 B 国人在一张有 n 个位置的圆桌上吃饭, A 国人不会坐在旁边有人的位置, B 国人不会坐在距离 2 以内有人的位置. 刚开始圆桌上没有人, 接下来会有 p / (p + q) 的概率走进一个 A 国人, q / (p + q) 的概率走进一个 B 国人; 他会随机选择一个位置, 如果不符合他的要求就直接离开. 问最后不能再容纳人时人数的期望, 对 998244353 取模. n ≤ 3e5, 0 ≤ p, q < 998244353.
思路: 首先第一个人坐下时不受任何限制, 且每个人坐下后是无差别的. 所以问题变成考虑剩下的 n - 1 个连续的位置能容纳多少人. 定义 dp[i] 为连续 i 个位置能容纳人的期望, p 不为 0 时有 dp[1] = dp[2] = 0, dp[3] = dp[4] = 1(p 为 0 时dp[3] = dp[4] = 0), 状态转移时枚举分隔位置即可. 推导出 \[dp[j] = \frac{p × \sum_{i=2}^{j-1}{(dp[i - 1] + dp[j - i])} + q × \sum_{i=3}^{j-2}{(dp[i - 1] + dp[j - i])}} { p × (j - 2) + q × (j - 4) } = \frac{2p × sdp[j - 2] + 2q × sdp[j - 3]} {p × (j - 2) + q × (j - 4)} \]
(sdp[] 是 dp[] 的前缀和)
view code
#include <bits/stdc++.h> using namespace std; #define ll long long #define inc(i, l, r) for (int i = l; i <= r; i++) const int maxn = 3e5 + 5; const int mod = 998244353; ll dp[maxn], s[maxn]; int n; ll p, q; inline ll ksm(ll a, ll n) { ll r = 1; while (n) { if (n & 1) r = r * a % mod; a = a * a % mod; n >>= 1; } return r; } int main() { scanf("%d %lld %lld", &n, &p, &q); if (p) dp[3] = dp[4] = s[3] = 1, s[4] = 2; inc(i, 5, n - 1) { dp[i] = ((2 * p * s[i - 2] + 2 * q * s[i - 3]) % mod * ksm((p * (i - 2) + q * (i - 4)) % mod, mod - 2LL) + 1) % mod; s[i] = (s[i - 1] + dp[i]) % mod; } printf("%lld\n", (dp[n - 1] + 1) % mod); }