AtCoder Beginner Contest 243 題解
A - Shampoo
題目描述:給你\(V\)升水,有三個人,依次用\(A , B , C\)升,迴圈使用,水不會補充,求誰使用的時候水不夠。
思路:先用\(V\)模\(A + B + C\)然後再討論即可
時間複雜度:\(O(1)\)
參考程式碼:
void solve() { int V, A, B, C; cin >> V >> A >> B >> C; int sum = 0; sum = A + B + C; V %= sum; if (V < A) cout << "F" << '\n'; else if (V < A + B) cout << "M" << '\n'; else cout << "T" << '\n'; return; }
B - Hit and Blow
題目描述:給你兩個長度為\(n\)的陣列\(a , b\),求分別滿足條件:\(a_i = b_j , i = j , \forall 1 \leq i \leq n , 1 \leq j \leq n\) 的\((i , j)\)和\(a_i = b_j , i \neq j , \forall 1 \leq i \leq n , 1 \leq j \leq n\)的\((i , j)\)的數量。
思路:考慮到\(1 \leq n \leq 1000\),所以暴力列舉每一對\((i , j)\)進行判斷即可
時間複雜度:\(O(n^2)\)
參考程式碼:
void solve() { int n; cin >> n; vector<int>a(n + 1, 0), b(n + 1, 0); for (int i = 1; i <= n; ++i) cin >> a[i]; for (int i = 1; i <= n; ++i) cin >> b[i]; int cnt = 0, ct = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { cnt += (a[i] == b[j] && i == j); ct += (a[i] == b[j] && i != j); } } cout << cnt << "\n" << ct << '\n'; return; }
C - Collision 2
題目描述:二維平面上給定一些運動的點,並給定其初始時的運動方向,運動方向只有沿著\(x\)軸的負方向或者正方向移動,問是否存在碰撞。
思路:首先不同\(y\)座標的點一定不會發生碰撞,故將\(y\)相同的點放一起討論,不發生碰撞只有可能是以下兩種情況:
- 所有點的初始時的運動方向一致
- 前半部分的運動方向是\(x\)的負方向,後半部分是\(x\)的正方向
故根據這兩種情況討論即可,考慮到\(y\)很大,使用unordered_map
對映一下即可。
時間複雜度:\(O(nlogn)\)
參考程式碼:
void solve() { int n; cin >> n; using PII = pair<int, int>; unordered_map<int, vector<PII>>mp; set<int>s; int x, y; for (int i = 1; i <= n; ++i) { cin >> x >> y; mp[y].push_back({ x , i - 1 }); s.insert(y); } for (auto& iter : s) sort(mp[iter].begin(), mp[iter].end()); string str; cin >> str; for (auto& iter : s) { auto& vec = mp[iter]; char c = str[vec[0].second]; int cnt = 1; for (auto& v : vec) { if (c != str[v.second]){ if (cnt == 0 || c == 'R') { cout << "Yes\n"; return; } else { cnt = 0; c = str[v.second]; } } } } cout << "No" << '\n'; return; }
D - Moves on Binary Tree
題目描述:給你一個無限大的二叉樹,二叉樹的結點編號滿足條件:若父結點的編號為\(i\),則左兒子的編號為\(2i\),右兒子的編號為\(2i + 1\),給定一個移動命令和初始位置編號\(x\),迴圈執行\(n\)次,問執行完畢後所在位置的編號,最終答案不超過\(10^{18}\)。
思路:比較簡單的模擬題,考慮到過程中可能會超過long long
的最大表示範圍,所以將\(x\)轉化成二進位制陣列,然後進行模擬。
時間複雜度:\(O(n)\)
參考程式碼:
void solve() {
long long n, x;
string s;
cin >> n >> x >> s;
int m = s.size(), idx = 0;
vector<int>a;
while (x) {
a.push_back(x % 2);
x /= 2;
}
reverse(a.begin(), a.end());
int id = a.size() - 1;
for (int i = 1; i <= n; ++i) a.push_back(0);
for (int i = 1; i <= n; ++i) {
if (s[idx] == 'U') --id;
else if (s[idx] == 'L') a[++id] = 0;
else a[++id] = 1;
idx = (idx + 1) % m;
}
for (int i = 0; i <= id; ++i) x = (x << 1) + a[i];
cout << x << '\n';
return;
}
E - Edge Deletion
題目描述:給你\(n\)個點\(m\)條邊的無向連通圖,問最多刪除多少條邊,使得剩下的圖滿足以下條件:
- 圖仍然連通
- 刪除邊後的圖中任意兩點之間的距離等於沒刪邊的圖中任意兩點之間的距離
思路:首先可以考慮刪除某條邊,然後進行檢驗,時間複雜度:\(O(n^5)\)。我們可以考慮先使用Floyd
演算法求出任意兩點之間的最短距離定義\(f_{i , j}\)表示頂點\(i , j\)之間的最短距離,然後我們列舉每一條邊\((u , v , c)\),若\(\exist 1 \leq i \leq n , i \neq u , i \neq v\)使得\(f_{u , i} + f_{i , v} \leq c\),那說明該條邊是可以刪去的,考慮到要排除\(i\)與\(u ,v\)相等的情況,可以將\(f_{i , i}\)的初始值設定為\(\infin\)用於簡化程式碼:
時間複雜度:\(O(n^3)\)
參考程式碼:
void solve() {
int n, m;
cin >> n >> m;
vector<tuple<int, int, int>>edges;
vector<vector<long long>>f(n + 1, vector<long long>(n + 1, 0x3f3f3f3f3f3f3fll));
int u, v, w;
for (int i = 1; i <= m; ++i) {
cin >> u >> v >> w;
edges.push_back({ u ,v , w });
f[u][v] = f[v][u] = w;
}
for (int k = 1; k <= n; ++k) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}
}
}
int res = 0;
for (auto&& [u, v, w] : edges) {
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (f[u][i] + f[i][v] <= w) cnt = 1;
}
res += cnt;
}
cout << res << '\n';
return;
}