上週去玩了(逃
A - Piling Up (abc363 A)
題目大意
給定分數,問晉級還差多少分。分別到\(100,200,300\)分能晉級。
解題思路
找到第一個大於當前分數的,其差即為答案。
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int a;
cin >> a;
vector<int> rk{100, 200, 300};
auto it = upper_bound(rk.begin(), rk.end(), a);
int ans = *it - a;
cout << ans << '\n';
return 0;
}
B - Japanese Cursed Doll (abc363 B)
題目大意
給定\(n\)個人的頭髮長度,每天會漲\(1\)釐米。問至少經過多少天,有至少\(P\)個人的頭髮長度至少是\(T\)。
解題思路
由於頭髮都是一起漲的,將頭髮長度從長到短排序,當第\(P\)個人的頭髮漲到\(T\)時,前\(P-1\)個也至少是\(T\)了。所以答案就是第\(P\)個人的頭髮漲到\(T\)的時間。
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, t, p;
cin >> n >> t >> p;
vector<int> a(n);
for (auto& x : a) {
cin >> x;
}
sort(a.begin(), a.end(), greater<int>());
int ans = max(0, t - a[p - 1]);
cout << ans << '\n';
return 0;
}
C - Avoid K Palindrome 2 (abc363 C)
題目大意
給定一個字串,將字母排序,問有多少種情況,其不存在一個長度為\(k\)的迴文串。
解題思路
字串長度\(n\)只有\(10\),花\(O(10!)\)列舉所有的排列情況,然後花\(O(n)\)列舉子串,再花\(O(k)\)判斷是否是迴文串即可。時間複雜度是\(O(n!nk)\)
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, k;
string s;
cin >> n >> k >> s;
int ans = 0;
auto ispalindrome = [&](string&& s) -> bool {
auto t = s;
reverse(t.begin(), t.end());
return t == s;
};
auto check = [&](string s) -> bool {
for (int i = 0; i <= n - k; i++) {
if (ispalindrome(s.substr(i, k))) {
return false;
}
}
return true;
};
sort(s.begin(), s.end());
do {
ans += check(s);
} while (next_permutation(s.begin(), s.end()));
cout << ans << '\n';
return 0;
}
D - Palindromic Number (abc363 D)
題目大意
求第\(n\)小的迴文數字。
解題思路
觀察迴文數字,按長度分奇數和偶數兩種情況。
- 1 2 3 4 5 6 7 8 9
- 11 22 33 44 55 66 77 88 99
- 101 111 121 131 141 151 ... 191
- 202 212 222 232 242 252 ... 292
- ...
- 909 919 929 939 949 959 ... 999
- 1001 1111 1221 1331 1441 ... 1991
- 2002 2112 2222 2332 2442 ... 2992
- ...
- 9009 9119 9229 9339 9449 ... 9999
由於是前後是對稱的,由於數的比較是從高位比較,我們可以把右半部分的低位忽略,這樣就變成
- 1 2 3 4 5 6 7 8 9
- 1 2 3 4 5 6 7 8 9
- 10 11 12 13 14 15 ... 19
- 20 21 22 23 24 25 ... 29
- ...
- 90 91 92 93 94 95 ... 99
- 10 11 12 13 14 15 ... 19
- 20 21 22 23 24 25 ... 29
- ...
- 90 91 92 93 94 95 ... 99
長度是奇偶迴圈的,去除右半部分,容易發現它就是
- 1 2 3 4 5 6 7 8 9
- 10 11 12 13 14 15 16 17 18 19 ... 90 91 92 93 94 95 96 97 98 99
- 100 101 .......
每行(即一個長度)多出現一次。而依次每個長度的數量分別是\(9,99,999,9999......\)。
因此透過\(n\),得知道第\(n\)個數的長度,以及是該長度的奇還是偶。如何知道長度,列舉即可,其長度不會超過\(O(\log n)\)。
知道該長度後,就知道該長度有多少個數字\(p\),再透過\(n \geq p\)來判斷是偶數情況還是奇數情況,\(n \% p\)就是對應的數了。這裡的\(n\)是減去小的長度後的值,即從\(1000\)(如果長度是\(4\))開始數的第幾個數。
程式碼中 s=to_string(n % p + p / 9)
,其中\(p/9\)是因為數是從\(1000\)開始的,而不是\(0000\)開始。
特判一下\(0\)。
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
LL n;
cin >> n;
if (n == 1) {
cout << "0\n";
return 0;
}
n -= 2;
LL p = 9;
while (n >= 2 * p) {
n -= 2 * p;
p *= 10;
}
auto s = to_string(n % p + p / 9);
auto t = s;
reverse(t.begin(), t.end());
if (n < p)
s.pop_back();
auto ans = s + t;
cout << ans << '\n';
return 0;
}
E - Sinking Land (abc363 E)
題目大意
方格島,四面環海。
給出島的高度,每年海平面上升\(1\)。
問\(y\)年的每一年,沒被淹的島的數量。
解題思路
起初是四周的島可能會被淹,稱之為危險島,我們需要比較危險島的高度和海平面高度。
因為越矮的島越容易被淹,我們優先比較危險島的高度最低的,如果被淹了,則會新增一些危險島,然後繼續比較高度最低的,直到沒被淹。
因此用優先佇列維護這些危險島的高度,當有新的危險島被淹時,新增周圍變成危險島的高度,模擬即可。
時間複雜度為\(O(hw\log hw)\)
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int h, w, y;
cin >> h >> w >> y;
vector<vector<int>> a(h, vector<int>(w));
for (auto& i : a)
for (auto& j : i)
cin >> j;
priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>,
greater<tuple<int, int, int>>>
pq;
vector<vector<int>> vis(h, vector<int>(w, 0));
auto push = [&](int x, int y) {
if (x >= 0 && x < h && y >= 0 && y < w && !vis[x][y]) {
vis[x][y] = 1;
pq.push({a[x][y], x, y});
}
};
for (int i = 0; i < h; i++) {
push(i, 0);
push(i, w - 1);
}
for (int i = 0; i < w; i++) {
push(0, i);
push(h - 1, i);
}
array<int, 4> dx = {0, 0, 1, -1};
array<int, 4> dy = {1, -1, 0, 0};
int ans = h * w;
for (int i = 1; i <= y; i++) {
while (!pq.empty()) {
auto [v, x, y] = pq.top();
if (v <= i) {
pq.pop();
ans--;
for (int j = 0; j < 4; j++) {
int nx = x + dx[j];
int ny = y + dy[j];
push(nx, ny);
}
} else {
break;
}
}
cout << ans << '\n';
}
return 0;
}
F - Palindromic Expression (abc363 F)
題目大意
給定\(n\),將\(n\)拆成若干個數相乘的表示式,其中這個表示式是個迴文串,且不存在數字\(0\)。
解題思路
考慮樸素搜尋,每個表示式的每個乘數必定是\(n\)的因子,因此我們事先預處理出\(n\)的所有因子,分析可知我們只需預處理\(< \sqrt{n}\)的因子即可,大於\(\sqrt{n}\)的因子要麼其迴文數\(< \sqrt{n}\),要麼兩個數乘起來會\(> n\)了。
然後就列舉乘數是什麼,即列舉\(n\)的因子\(x\),然後求其迴文數字\(y\),判斷\(x\)和\(xy\)是否都是\(n\)的因子。是的話,則就是\(n = x * \frac{n}{xy} * y\),接下來就是判斷\(\frac{n}{xy}\)能否表示成一個迴文表示式,這就是一個子問題了,記憶化搜尋一下即可。
考慮其複雜度,搜尋狀態的\(n\)的取值只有因數個,每次搜尋列舉因數,因此總的時間複雜度是\(O(\sigma^2(n))\),但實際會遠小於這個數。
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
LL n;
cin >> n;
vector<int> fac;
for (LL i = 2; i * i <= n; i++) {
if (n % i == 0) {
fac.push_back(i);
}
}
map<LL, pair<LL, LL>> dp;
auto rev = [](LL x) {
string s = to_string(x);
reverse(s.begin(), s.end());
return stoll(s);
};
auto is_palindromic = [&](LL x) { return x == rev(x); };
auto is_valid = [&](LL x) {
return to_string(x).find('0') == string::npos;
};
auto dfs = [&](auto dfs, LL n) -> bool {
if (is_valid(n) && is_palindromic(n))
return true;
if (dp.count(n))
return true;
for (auto& x : fac) {
auto y = rev(x);
if (n % x == 0 && n / x % y == 0 && is_valid(x) && is_valid(y) &&
dfs(dfs, n / x / y)) {
dp[n] = {x, y};
return true;
}
}
return false;
};
dfs(dfs, n);
if (dp.empty() && (!is_valid(n) || !is_palindromic(n)))
cout << -1 << '\n';
else {
string ansl, ansr;
while (dp.count(n)) {
auto [x, y] = dp[n];
ansr = ansr + "*" + to_string(x);
ansl = to_string(y) + "*" + ansl;
n /= x;
n /= y;
}
string ans = ansl + to_string(n) + ansr;
cout << ans << '\n';
}
return 0;
}
G - Dynamic Scheduling (abc363 G)
題目大意
給定\(n\)個任務的截止完成時間\(d_i\)和獎勵\(p_i\),指如果該任務能在\(d_i\)之前完成,則能獲得獎勵\(p_i\)。
決定每天完成什麼任務,使得獎勵最大化。
維護\(q\)次操作,每次操作修改一個任務的\(d_i\)和\(p_i\),修改後求上述答案,修改持久化。
解題思路
<++>
神奇的程式碼