A. Road To Zero
兩個數,將其中一個增加或減少1的代價為a,同時增加或減少1的代價為b,求將兩個數同時變為0的最小代價。
當一個數為0或者兩個數異號時,最優為只執行代價為a的操作,當兩個數同號時,要考慮b與a*2的大小。

#include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i, a, b) for (register int i = a; i <= b; i++) ll x, y, a, b; int main() { int t; cin >> t; while (t--) { cin >> x >> y; cin >> a >> b; if (b > a * 2 || x * y <= 0) cout << (abs(x) + abs(y)) * a << endl; else { x = abs(x); y = abs(y); cout << min(x, y) * b + abs(y - x) * a << endl; } } }
B. Binary Period
利用字符串t構造s使t為s的一個子序列,並且使周期k最小,長度不大於t的兩倍。
根據題意這樣構造出來k只能是1或2。如果t本身具有周期1,直接輸出,否則可以在兩個相同元素中間插另一個元素,此時s周期為2。

#include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i, a, b) for (register int i = a; i <= b; i++) int n; char s[110]; int main() { int t; cin >> t; while (t--) { memset(s, 0, sizeof(s)); cin >> s; n = strlen(s); int flag = 1; rep(i, 1, n - 1) if (s[i] != s[i - 1]) flag = 0; if (flag) cout << s; else rep(i, 0, n - 1) { cout << s[i]; if (s[i] == s[i + 1]) putchar(s[i] == '0' ? '1' : '0'); } cout << endl; } }
C. Yet Another Counting Problem
x∈[l,r], ((x mod a) mod b)≠((x mod b) mod a) 的個數。
考慮預處理前綴和,但是l,r實在是太大了。發現x變化具有周期性,周期為a*b,只用預處理[0,a*b]即可。

#include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i, a, b) for (register int i = a; i <= b; i++) ll a, b, q, num[40010]; ll l, r, ans; int main() { int t; cin >> t; while (t--) { cin >> a >> b >> q; rep(i, 1, a * b) { num[i] = (i % a % b != i % b % a) ? 1 : 0; num[i] += num[i - 1]; } while (q--) { cin >> l >> r; ans = (r - l) / (a * b) * num[a * b]; l %= (a * b); r %= (a * b); if (r >= l) cout << ans + num[r] - num[l - 1] << " "; else cout << ans + num[r] + num[a * b] - num[l - 1] << " "; } cout << endl; } }
D. Multiple Testcases
比賽時讀了好久才明白題意(wtcl),組合m數組,在滿足條件的情況下使ans(組合的數量)最小,條件為一個組合中大於等於i的數不超過c[i]個。
ans挺好找。然后每個組合的元素,按照m[i]從大到小往里面填就行了。

#include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i, a, b) for (register int i = a; i <= b; i++) ll n, k, m[200010], c[200010]; ll ans; vector<ll> tmp[200010]; int main() { cin >> n >> k; rep(i, 1, n) cin >> m[i]; rep(i, 1, k) cin >> c[i]; sort(m + 1, m + n + 1); for (int i = n; i; i--) { if (m[i] == m[i - 1]) continue; ans = max(ans, (ll)ceil(1.0 * (n - i + 1) / c[m[i]])); } for (int i = n, j = 0; i; i--, j = (j + 1) % ans) tmp[j].push_back(m[i]); cout << ans << endl; rep(i, 0, ans - 1) { int j = tmp[i].size(); cout << j << " "; rep(z, 0, j - 1) cout << tmp[i][z] << " "; cout << endl; } }
E. Placing Rooks
n*n的格子放n個車,要求所有空格子都在攻擊范圍、k對車能互相攻擊。
所有格子都在攻擊范圍,那么每一行 / 每一列都要有車。
k對車能相互攻擊,那么要空k列不能放車(保證每一行都有一輛車),或者空k行不放車(保證每一列都有車)。
先計算行,問題相當於在n-k列中放n個車,答案為
至於為什么是這樣計算可以先學習特斯林數和“n個球m個盒子問題”。
當k==0的時候行和列的答案重復了,所以k!=0的時候答案乘2。k>=n時答案為0。

#include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i, a, b) for (register int i = a; i <= b; i++) const int mod = 998244353; ll n, k; ll ans; ll fac[200010]; ll ksm(ll a, ll b) { ll res = 1; for (; b; b >>= 1, a = a * a % mod) if (b & 1) res = res * a % mod; return res; } ll C(ll x, ll y) { return fac[x] * ksm(fac[y] * fac[x - y] % mod, mod - 2) % mod; } int main() { fac[0] = 1; cin >> n >> k; rep(i, 1, n) fac[i] = fac[i - 1] * i % mod; if (k >= n) { puts("0\n"); return 0; } rep(i, 0, n - k) { ll flag = i & 1 ? -1 : 1; ll tmp = flag * (C(n - k, i) * ksm(n - k - i, n) % mod); ans = (ans + tmp + mod) % mod; } if (k == 0) cout << ans << endl; else cout << (ans * C(n, n - k) % mod) * 2 % mod << endl; }