Atcoder Beginner Contest 406
比赛链接:ABC406
A - Not Acceptable
将小时转换成分钟直接进行判断。
时间复杂度: O ( 1 ) O(1) O(1)。
#include <bits/stdc++.h>
using namespace std;int main() {ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);int a, b, c, d;cin >> a >> b >> c >> d;if (a * 60 + b >= c * 60 + d) cout << "Yes" << endl;else cout << "No" << endl;return 0;
}
B - Product Calculator
根据题意进行模拟,为了避免 overflow 可以使用 __int128_t
来储存计算机当前展示的值。
时间复杂度: O ( N + K ) O(N + K) O(N+K)。
#include <bits/stdc++.h>
using namespace std;#define int long longconst int N = 105;
int n, k, a[N];signed main() {ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);cin >> n >> k;for (int i = 1; i <= n; i++) cin >> a[i];__int128_t now = 1;int inf = 0;for (int i = 1; i <= k; i++) inf = inf * 10 + 9;for (int i = 1; i <= n; i++) {if (now > inf / a[i]) now = 1;else now *= a[i];}string ans;while (now) ans.push_back('0' + now % 10), now /= 10;for (int i = ans.size() - 1; i >= 0; i--) cout << ans[i];cout << endl;return 0;
}
C - ~
- 根据题目条件可知,要找的波浪形的子数组,一定先增大在减小最后再增大。
- 我们只需要找到一个连续递减部分,再在这个连续递减的部分两侧加上至少一个数就能构造出满足题目条件的子数组。
- 每一个连续递减部分能构成的满足题目条件的子数组的数量为这个部分 前面连续递增的长度 × \times × 后面连续递增的长度。
- 因此,只需要找到原数组每个连续递增部分的长度,答案即为所有两个相邻的连续递增部分的长度的乘积的和。
时间复杂度: O ( N ) O(N) O(N)。
#include <bits/stdc++.h>
using namespace std;using ll = long long;
const int N = 3e5 + 5;
int n, p[N], cnt[N];int main() {ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);cin >> n;for (int i = 1; i <= n; i++) cin >> p[i];int num = 1;for (int i = 2; i <= n; i++) {if (p[i] > p[i - 1]) cnt[num]++;else if (cnt[num]) num++;}ll ans = 0;for (int i = 1; i <= num; i++) ans += 1LL * cnt[i - 1] * cnt[i];cout << ans << endl;return 0;
}
D - Garbage Removal
对于每一个行、列分别用一个 set
维护这一行中的点的纵、横坐标,每次查询完将对应的点的坐标从 set
中删除。
时间复杂度: O ( ∑ i = 1 Q T log N + N log N ) O(\sum_{i = 1}^Q T\log N + N\log N) O(∑i=1QTlogN+NlogN),其中 T T T 是每次需要删除的点的数量,并且 ∑ i = 1 Q T log N ≤ N log N \sum_{i = 1}^Q T\log N \le N\log N ∑i=1QTlogN≤NlogN。
#include <bits/stdc++.h>
using namespace std;const int N = 2e5 + 10;
int h, w, n, q;
set<int> row[N], col[N];int main() {ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);cin >> h >> w >> n;for (int i = 1; i <= n; i++) {int x, y;cin >> x >> y;row[x].insert(y), col[y].insert(x);}cin >> q;while (q--) {int op, idx;cin >> op >> idx;if (op == 1) {cout << row[idx].size() << "\n";for (int i : row[idx]) col[i].erase(idx);row[idx].clear();}if (op == 2) {cout << col[idx].size() << "\n";for (int i : col[idx]) row[i].erase(idx);col[idx].clear();}}return 0;
}
E - Popcount Sum 3
从高位往低位考虑,总共放置 k k k 个 1 1 1,利用 d f s dfs dfs 找出答案,具体实现看代码。
时间复杂度: O ( ∑ i = 1 T i log N ) O(\sum_{i = 1}^Ti\log N) O(∑i=1TilogN)。
从高位往地位进行搜索,
- 如果每一位都放 1 1 1,最多进行 log N \log N logN 次 s u m sum sum 就会大于 N N N。
- 如果前面都不放 1 1 1,最多进行 log N − K \log N - K logN−K 次就会被剪枝。
#include <bits/stdc++.h>
using namespace std;#define int long longconst int mod = 998244353;
int n, k, c[65][65];// 预处理组合数
void init() {c[0][0] = 1;for (int i = 1; i <= 60; i++) {c[i][0] = c[i][i] = 1;for (int j = 1; j < i; j++)c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;}
}// cur 是当前位置,sum 是当前的和,cnt 是剩余要放置的 1 的数量
int dfs(int cur, int sum, int cnt) { // 不合法条件:sum 大于 n,剩余位置无法放下需要放置的 1if (sum > n || cnt > cur + 1) return 0;// 所有 1 都放完了,sum 就是要加上的答案if (cnt == 0) return sum % mod;// 剩余的 cnt 个 1 可以放在 0 - p 任意一个位置if (sum + (1LL << (cur + 1)) - 1 <= n) {int oup = ((1LL << (cur + 1)) - 1) % mod * c[cur][cnt - 1] % mod;oup = (oup + c[cur + 1][cnt] * (sum % mod) % mod) % mod;return oup;}return (dfs(cur - 1, sum, cnt) + dfs(cur - 1, sum + (1LL << cur), cnt - 1)) % mod;
}void solve() {cin >> n >> k;cout << dfs(59, 0, k) << "\n";
}signed main() {ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);init();int T;cin >> T;while (T--) solve();return 0;
}