AtCoder Beginner Contest 238
\(A - F\) 題解
A - Exponential or Quadratic
題意
判斷 \(2^n > n^2\)是否成立?
Solution
當 \(n\) 為 2,3,4 的時候不成立,否則成立
Code
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
int n; cin >> n;
bool flag = true;
if(n >= 2 && n <= 4) flag = false;
puts(flag ? "Yes" : "No");
return 0;
}
B - Pizza
題意
切披薩,先在 12:00 的位置(鐘錶的位置) 切一刀,然後按照給定的序列 \(A\) , 每次先順時針旋轉 \(A_i\) 度,然後在在 12:00 的位置切一刀,問最後的所有披薩塊中圓心角最大的是多少度?
Solution
按照題意模擬,在原披薩中每個被切到的地方標記一下,求最大的區間長度即可
Code
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
int n; cin >> n;
vector<bool> vis(361);
vis[0] = vis[360] = true; //初始化
int res = 0;
for(int i = 0; i < n; i ++ ) {
int x; cin >> x;
res += x;
vis[res % 360] = true;
}
int l = 0; //上一次被切到的位置
int ans = 0;
for(int i = 1; i <= 360; i ++ ) { //注意要列舉到 360°
if(!vis[i]) continue;
ans = max(ans, i - l);
l = i;
}
cout << ans << "\n";
return 0;
}
C - digitnum
題意
定義\(f(x)\) 是 和 \(x\) 的位數相同且小於等於 \(x\) 的正整數的個數
\(f(1) = 1, f(2) = 2, f(10) = 1 \ ...\)
給定一個 \(x\) , 求 \(f(1) \ + \ f(2) \ + \ f(3) \ + \ ... \ f(x)\) 的值 模 \(998244353\) 的值
Solution
注意到對於長度確定的數字的 \(f(n)\) 的值是 \(1 - x\) 的一個公差為\(1\) 的序列
因此我們可以列舉數字的長度, 分別計算即可
Code
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
constexpr int MOD = 998244353;
LL chk(int x) {
LL res = 1;
while(x -- ) res *= 10;
return res;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.txt", "r", stdin);
#endif
LL x; cin >> x;
LL ans = 0;
int len = to_string(x).size();
for(int i = 1; i <= len; i ++ ) {
LL r = min(chk(i) - 1, x); //右邊界
LL l = chk(i - 1); //左邊界
LL len = r - l + 1;//計算等差數列的長度
LL X = len + 1; //等差數列計算公式 len + (len + 1) / 2
if(len % 2 == 0) len /= 2; //這裡為了避免溢位先除以 2
else X /= 2;
len %= MOD; //這裡為了避免溢位先取模
X %= MOD;
LL res = len * X % MOD; //上面已經除以 2 了
ans = (ans + res) % MOD;
}
cout << ans << "\n";
return 0;
}
D - AND and SUM
題意
\(x \ \& \ y \ = \ a\) \(x \ + \ y \ = s\)
給定 \(a\) 和 \(s\) , 判斷是否存在合法的 \(x\) 和 \(y\)
Solution
\(x + y = x \bigoplus y + (x \& y) * 2\) 異或是不進位加法, \(\&\) 是進位,因此要乘以 2
因此我們先判斷 \(a * 2 \le s\) 是否成立
然後判斷 \(s - 2 * a\) 二進位制下的每一位,如果第 \(i\) 位是 1 的話,那麼 a 的第 \(i\) 位就不可以是 1
Code
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
int T; cin >> T;
while(T -- ) {
LL a, s; cin >> a >> s;
LL res1 = a * 2;
if(a * 2 > s) {
puts("No");
continue;
}
LL p = s - a * 2; // x ^ y
bool flag = true;
for(int i = 0; i < 64; i ++ ) {
if(p >> i & 1) {
if(a >> i & 1) {
flag = false;
break;
}
}
}
puts(flag ? "Yes" : "No");
}
return 0;
}
E - Range Sums
題意
給定一些區間的和,判斷是否可以 在其中選擇一些區間 可以通過這些區間的和算出 \(1-n\) 的區間和
Solution
-
One \(DFS\)
問題可以抽象成一個圖論問題,給定的 \([l, r]\) 區間可以理解為 $l-1 $ 到 \(r\) 的一條無向邊,判斷從 \(0\) 是否可以到達 \(n\) 即可
-
Two \(DSU\)
對於給定的區間 \([l, r]\) 我們可以看成 點 \(l-1\) 和 點 \(r\) 是連通的,即可以互相到達的兩個點,用並查集合並這兩個點
判斷最終 \(0\) 和 \(n\) 是否連通即可
Code
-
One
#include <bits/stdc++.h> using namespace std; constexpr int N = 2e5 + 10; vector<int> e[N]; bool st[N]; void dfs(int u) { if(st[u]) return; st[u] = true; for(int &v: e[u]) { dfs(v); } } int main() { int n, q; cin >> n >> q; while(q -- ) { int x, y; cin >> x >> y; e[x - 1].push_back(y); e[y].push_back(x - 1); } dfs(0); puts(st[n] ? "Yes" : "No"); return 0; }
-
Two
#include <bits/stdc++.h> using namespace std; constexpr int N = 2e5 + 10; int main() { int n, q; cin >> n >> q; vector<int> p(N); iota(p.begin(), p.end(), 0); function<int(int)> find = [&](int x) { if(p[x] != x) p[x] = find(p[x]); return p[x]; }; while(q -- ) { int x, y; cin >> x >> y; p[find(x - 1)] = find(y); } puts(find(0) == find(n) ? "Yes" : "No"); return 0; }
F - Two Exams
題意
有兩個關於城市的測試 \(P\) 和 \(Q\) ,二者的結果都是 \(1-n\) 的一個排列
現在我們要再 \(n\) 個城市中選擇 \(k\) 個城市 並且滿足一下條件
- 如果 \(X\) 被選擇了 而且 \(Y\) 沒有被選擇, 那麼 $P_X > P_Y $ 並且 \(Q_X > Q_Y\)
Solution
我們可以先把兩個 測試分數 處理一下
定義一個 \(v\) 陣列, \(v_i = j\) 代表 測試\(P\)分數為 \(i\) 的人 測試 \(Q\) 為 \(j\)
對於 \(v\) 陣列我們可以進行一個 \(DP\)
\(Dp[i][j][k]\) 代表在 測試 \(P\) 分數區間為 \(1-i\) 的人中選擇 \(j\) 個人, 並且在已選擇的人中測試 \(Q\) 的分數最低為 \(k\)
那麼我們可以進行一個 \(O(n^3)\) 的 \(DP\)
constexpr int MOD = 998244353; dp[0][0][n] = 1; for(int i = 1; i <= n; i ++) { for(int j = 0; j <= k; j ++ ) { for(int val = 1; val <= n; val ++ ) { if(j < k && v[i] < val) { //如果可以選擇這個城市 dp[i][j + 1][val] = (dp[i][j + 1][val] + dp[i - 1][j][k]) % MOD; } //如果不選這個城市 也是需要更新的 //如果這個城市不選,那麼我們所選的合法方案中最小的值一定也比 val 大 dp[i][j][min(v[i], val)] = (dp[i][j][min(v[i], val)] + dp[i - 1][j][k]) % MOD; } } } int ans = 0; for(int &x: dp[n][k]) ans = (ans + x) % MOD; cout << ans << "\n";
在實際寫的過程中 \(dp\) 陣列的第一維 可以用滾動陣列優化
Code
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i ++ )
using namespace std;
typedef long long LL;
typedef pair<int, int> PII ;
template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }
constexpr int MOD = 998244353;
int main() {
int n, k;
cin >> n >> k;
vector<int> a(n), b(n);
for(int &x: a) cin >> x;
for(int &x: b) cin >> x;
vector<int> v(n);
rep(i, 0, n - 1) v[a[i] - 1] = b[i] - 1;
vector dp(k + 1, vector<int>(n + 1, 0));
dp[0][n] = 1;
rep(i, 0, n - 1) {
vector ndp(k + 1, vector<int>(n + 1, 0));
rep(x, 0, k) {
rep(y, 0, n) {
if(x < k && v[i] < y) { //如果可以選
ndp[x + 1][y] += dp[x][y];
ndp[x + 1][y] %= MOD;
}
ndp[x][min(y, v[i])] += dp[x][y];
ndp[x][min(y, v[i])] %= MOD;
}
}
dp.swap(ndp);
}
int res = 0;
for(int &x: dp[k]) {
res = (res + x) % MOD;
}
cout << res << "\n";
return 0;
}