从这里开始
Problem A Triangle
考虑把三角形移到和坐标轴相交,即
然后能够用坐标比较简单地计算面积,简单构造一下就行了。
Code
#include <bits/stdc++.h> using namespace std; typedef bool bolean; #define ll long long const int V = 1e9; ll S; int main() { scanf("%lld", &S); if (S == 1000000000000000000ll) { printf("%d 0 0 0 0 %d ", V, V); return 0; } int z = S / V; int y = V; int x = 1; int h = S % V; printf("0 %d %d 0 %d %d ", y, x, x + z, h); return 0; }
Problem B Do Not Duplicate
你发现 $x$ 如果还会再出现,会把之间的数都删掉,所以 $x$ 向它的后继连一条边。然后模拟一下就行了。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; #define ll long long const int N = 2e5 + 5; int n; ll K; int a[N], h[N]; int fa[N], c[N]; boolean vis[N]; int main() { scanf("%d%lld", &n, &K); for (int i = 0; i < n; i++) { scanf("%d", a + i); } for (int i = n - 1; ~i; i--) h[a[i]] = i + n; for (int i = n - 1; ~i; i--) { int suf = h[a[i]]; fa[i] = (suf + 1) % n; c[i] = (suf + 1) / n; h[a[i]] = i; } int p = 0, q; vector<int> stk; do { vis[p] = true; stk.push_back(p); p = fa[p]; } while (!vis[p]); int sumcost = 0; do { q = stk.back(); stk.pop_back(); sumcost += c[q]; } while (q ^ p); stk.clear(); int x = 0; while (K - c[x] >= 1 && (x ^ p)) K -= c[x], x = fa[x]; if (x == p) K = (K - 1) % sumcost + 1; while (K - c[x] >= 1) K -= c[x], x = fa[x]; memset(vis, false, sizeof(vis)); for (int i = x; i < n + (K - 1) * n; i++) { int z = a[i % n]; if (!vis[z]) { stk.push_back(z); vis[z] = true; } else { int y = 0; do { y = stk.back(); stk.pop_back(); vis[y] = false; } while (y ^ z); } } for (auto x : stk) { printf("%d ", x); } return 0; }
Problem C GP 2
考虑一些必要条件:
- 和为 $3M$
- 最大值不超过 $2M$
- 奇数个数不超过 $M$
可以对最大值和最大奇数或剩下的最大值进行讨论,然后用归纳法证明。
剩下是基础计数。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; #define ll long long void exgcd(int a, int b, int& x, int& y) { if (!b) { x = 1, y = 0; } else { exgcd(b, a % b, y, x); y -= (a / b) * x; } } int inv(int a, int n) { int x, y; exgcd(a, n, x, y); return (x < 0) ? (x + n) : (x); } const int Mod = 998244353; template <const int Mod = :: Mod> class Z { public: int v; Z() : v(0) { } Z(int x) : v(x){ } Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) { int x; return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x)); } friend Z operator - (const Z& a, const Z& b) { int x; return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x)); } friend Z operator * (const Z& a, const Z& b) { return Z(a.v * 1ll * b.v); } friend Z operator ~(const Z& a) { return inv(a.v, Mod); } friend Z operator - (const Z& a) { return Z(0) - a; } Z& operator += (Z b) { return *this = *this + b; } Z& operator -= (Z b) { return *this = *this - b; } Z& operator *= (Z b) { return *this = *this * b; } friend boolean operator == (const Z& a, const Z& b) { return a.v == b.v; } }; Z<> qpow(Z<> a, int p) { Z<> rt = Z<>(1), pa = a; for ( ; p; p >>= 1, pa = pa * pa) { if (p & 1) { rt = rt * pa; } } return rt; } typedef Z<> Zi; const int N = 3e6 + 5; int n, m; Zi fac[N], _fac[N]; void prepare(int n) { fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i; _fac[n] = ~fac[n]; for (int i = n; i; i--) _fac[i - 1] = _fac[i] * i; } Zi comb(int n, int m) { return (n < m) ? (0) : (fac[n] * _fac[m] * _fac[n - m]); } int main() { scanf("%d%d", &n, &m); prepare(3 * m + n - 1); Zi ans = comb(3 * m + n - 1, n - 1) - comb(m + n - 2, n - 1) * n; for (int num = m + 1; num <= n && num <= 3 * m; num++) { if ((3 * m - num) & 1) continue; int sum = (3 * m - num) >> 1; Zi tmp = comb(sum + n - 1, n - 1); // mx is odd tmp -= num * comb(sum - m + n - 1, n - 1); // mx is even tmp -= (n - num) * comb(sum - m + n - 2, n - 1); tmp *= comb(n, num); ans -= tmp; } printf("%d ", ans.v); return 0; }
Problem D Negative Cycle
设到第 $i$ 个点的最短路为 $d_i$。不难发现 $d_i geqslant d_{i + 1}$,如果存在负环,那么会更新。
考虑最短路等于 $d$ 和最短路等于 $d + 1$ 的两段 $[a, b]$,以及 $(b, c]$。
那么 $(b, c]$ 不能有到 $a$ 之前的边,否则 $d$ 会被更新。$(b + 1, c]$ 之间不能有从前连向后面的边,否则 $d$ 也会被更新。
状态中记入 $a, b$,稍作优化就能做到 $O(n^3)$。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; #define ll long long const ll llf = (signed ll) (~0ull >> 2); const int N = 505; int n; int A[N][N]; ll s[N][N]; ll dp[N][N]; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i == j) { continue; } scanf("%d", A[i] + j); s[i][j] = A[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]; } } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) dp[i][j] = llf; dp[1][1] = 0; for (int i = 2; i <= n; i++) { dp[1][i] = dp[1][i - 1]; for (int j = 1; j < i; j++) { dp[1][i] += A[j][i]; } } for (int i = 2; i <= n; i++) { ll cost = 0; for (int b = i - 1; b; b--) { for (int j = b + 2; j <= i; j++) cost += A[b + 1][j]; for (int a = b; a; a--) { dp[b + 1][i] = min(dp[b + 1][i], dp[a][b] + cost + s[i][a - 1] - s[b][a - 1]); } } } ll ans = llf; for (int i = 1; i <= n; i++) ans = min(ans, dp[i][n]); printf("%lld ", ans); return 0; }
Problem E ABC String
先将连续的字符缩起来,设出现的次数满足 $a leqslant b leqslant c$。
现在我们希望删掉一些 $a$ 使得存在一种方案使得删掉一些 $b, c$ 使得 $a = b = c$。
不难发现,删掉一个 A 后至多使 $b$ 或 $c$ 减少 1。因此 A 仍然是出现次数最少的字符。
假设删除一些 A 之后,仍然满足 $b leqslant c$。
如果 $b = c$,那么每次删掉 BC 或者 CB 就行了。
考虑 $b - c$ 所能到达的最小值,如果一段中含 B,那么可以贡献 -1,如果只有一个 C,那么会贡献 1。
因此,我们希望删掉一些 A,使得 $b - c$ 的最小值能小于等于 0。
不难发现,只用删掉若干个单独的 C 之前的 A,并且删掉一个 A 至多使单独的 C 减少 1 个。
Code
#include<bits/stdc++.h> using namespace std; typedef bool boolean; const int N = 1e6 + 5; int n; char s[N], s1[N]; void shrink() { int t = 0; for (int i = 1; i <= n; i++) { if (s[i] ^ s[i - 1]) { s[++t] = s[i]; } } n = t; } int cnt[3]; char pg[3], qg[3]; void trans() { for (int i = 1; i <= n; i++) { cnt[s[i] - 'A']++; } vector<pair<int, int>> a; a.emplace_back(cnt[0], 0); a.emplace_back(cnt[1], 1); a.emplace_back(cnt[2], 2); sort(a.begin(), a.end()); #define make_trans(x, y) pg[x] = y, qg[y] = x; for (int i = 0; i < 3; i++) make_trans(a[i].second, i); for (int i = 1; i <= n; i++) { s[i] = pg[s[i] - 'A'] + 'A'; } } void make_valid() { int x = 0, y = 0; auto count = [&] (int l, int r) -> boolean { if (l > r) return false; for (int i = l; i <= r; i++) { if (s[i] == 'B') { x++; return false; } } if (l == 1 || r == n) return false; y++; return true; }; int ls = 0; vector<int> pos; for (int i = 1; i <= n; i++) { if (s[i] == 'A') { if (count(ls + 1, i - 1) && ls) { pos.push_back(i); } ls = i; } } count(ls + 1, n); if (x < y) { int t = 0; pos.resize(y - x); reverse(pos.begin(), pos.end()); for (int i = 1; i <= n; i++) { if (!pos.empty() && i == pos.back()) { pos.pop_back(); } else { s[++t] = s[i]; } } n = t; shrink(); s[n + 1] = 0; } memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; i++) cnt[s[i] - 'A']++; int d = cnt[2] - cnt[1]; int t = 0; char target = 'C'; (d < 0) && (d = -d, target = 'B'); ls = 0; auto remove = [&] (int l, int r) { if (l > r) return; if ((l == 1 || r == n) || (l ^ r)) { if (s[l] == target && d) l++, d--; if (l < r && s[r] == target && d) r--, d--; } for (int i = l; i <= r; i++) s1[++t] = s[i]; }; for (int i = 1; i <= n; i++) { if (s[i] == 'A') { remove(ls + 1, i - 1); s1[++t] = 'A'; ls = i; } } remove(ls + 1, n); n = t; for (int i = 1; i <= n; i++) { s[i] = s1[i]; } s[n + 1] = 0; } void make_equal() { memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; i++) cnt[s[i] - 'A']++; assert(cnt[1] == cnt[2]); int d = cnt[1] - cnt[0]; int t = 0; vector<pair<int, int>> pos; for (int i = 1; i <= n; i++) { if (i < n && d && s[i] != 'A' && s[i + 1] != 'A') { if (s1[t] == 'A' && s[i + 2] == 'A') { pos.emplace_back(t + 1, t + 2); s1[++t] = s[i]; } else { i++, d--; } } else { s1[++t] = s[i]; } } n = t; for (int i = 1; i <= n; i++) s[i] = s1[i]; vector<int> delpos; for (int i = 0; i < d; i++) { delpos.push_back(pos[2 * i].first); delpos.push_back(pos[2 * i + 1].second); } t = 0; reverse(delpos.begin(), delpos.end()); for (int i = 1; i <= n; i++) { if (!delpos.empty() && delpos.back() == i) { delpos.pop_back(); } else { s[++t] = s[i]; } } t = n; assert(delpos.empty()); } int main() { scanf("%s", s + 1); n = strlen(s + 1); shrink(); trans(); make_valid(); make_equal(); for (int i = 1; i <= n; i++) s[i] = qg[s[i] - 'A'] + 'A'; s[n + 1] = 0; puts(s + 1); return 0; }
Problem F Square Constraints
考虑每个位置有一个上界 $R_i$ 和一个下界 $L_i$。对于后 $N$ 个位置下界为 0。
考虑只有上界,我们把上界从小到大排序,那么答案等于 $prod_{i = 0}^{2N - 1} (R_i - i)$。
考虑对一些下界进行容斥。不难注意到下界非 0 的位置,它的上界是排在后 $N$ 个位置的上界之后。
所以只用枚举一下有多少个数被硬点不合法就可以 dp 了。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; #define pii pair<int, int> #define ll long long const int N = 505; int n, n2, Mod; pii a[N]; int cnt[N]; int dp[2][N]; int main() { scanf("%d%d", &n, &Mod); n2 = n << 1; int p1 = 0, p2 = 0, N4 = 4 * n * n, N2 = n * n; for (int i = n2 - 1; ~i; i--) { while (p1 < n2 && p1 * p1 <= N4 - i * i) p1++; while (p2 * p2 < N2 - i * i) p2++; if (p2) { a[i] = pii(p2, p1); } else { a[i] = pii(p1, 0); } } sort(a, a + n2); cnt[0] = 0; for (int i = 0; i < n2; i++) cnt[i + 1] = cnt[i] + !a[i].second; ll ans = 0; for (int i = 0; i <= n; i++) { int cur = 0; memset(dp[0], 0, sizeof(dp[0])); dp[0][0] = 1; for (int j = 0; j < n2; j++) { memset(dp[cur ^= 1], 0, sizeof(dp[0])); for (int k = 0; k <= i; k++) { if (!a[j].second) { dp[cur][k] = (dp[cur][k] + 1ll * (a[j].first - k - cnt[j]) * dp[cur ^ 1][k]) % Mod; } else { dp[cur][k + 1] = (dp[cur][k + 1] - 1ll * (a[j].first - k - cnt[j]) * dp[cur ^ 1][k]) % Mod; dp[cur][k] = (dp[cur][k] + 1ll * (a[j].second - (n + i + j - cnt[j] - k)) * dp[cur ^ 1][k]) % Mod; } } } ans += dp[cur][i]; } ans %= Mod; (ans < 0) && (ans += Mod); printf("%d ", (int) ans); return 0; }