A - Shout Everyday (abc367 A)
題目大意
高橋從\(A\)睡到 \(B\),如果在 \(C\)時,他醒著,他則會對章魚燒發癲,問他今天是否發癲。
解題思路
由於只有\(24\)小時,直接列舉 \(A \to B\),看看是否遍歷到 \(C\)即可。
神奇的程式碼
#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, b, c;
cin >> a >> b >> c;
bool ok = true;
for (; a != b; a = (a + 1) % 24) {
if (a == c) {
ok = false;
break;
}
}
if (ok)
cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}
B - Cut .0 (abc367 B)
題目大意
給定一個小數,將末尾零刪掉,如果小數部分刪掉則把小數部分刪掉。
解題思路
按照題意模擬即可。
神奇的程式碼
#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);
string s;
cin >> s;
while (s.back() == '0')
s.pop_back();
if (s.back() == '.')
s.pop_back();
cout << s << '\n';
return 0;
}
C - Enumerate Sequences (abc367 C)
題目大意
輸出所有符合要求的陣列\(a\),滿足
- \(a\)的和是 \(k\)的倍數
- \(a_i\)的範圍是 \(1 \sim r_i\)
解題思路
用\(dfs\)列舉所有的情況即可。
神奇的程式碼
#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;
cin >> n >> k;
vector<int> a(n);
for (auto& i : a)
cin >> i;
vector<int> ans(n);
auto dfs = [&](auto&& self, int pos) -> void {
if (pos == n) {
if (accumulate(ans.begin(), ans.end(), 0) % k == 0) {
for (auto i : ans)
cout << i << " ";
cout << '\n';
}
return;
}
for (int i = 1; i <= a[pos]; ++i) {
ans[pos] = i;
self(self, pos + 1);
}
};
dfs(dfs, 0);
return 0;
}
D - Pedometer (abc367 D)
題目大意
環形湖的\(n\)個點,給定 \(a_i\)表示從第 \(i\)個點順時針走到第 \(i+1\)個點的時間。
問對數 \((s,t)\),滿足從 \(s\)順時針 \(\to t\),使得其時間是\(m\)的倍數。
解題思路
考慮\(s \leq t\)的情況,其實就可以假象在一條數軸上,從左走到右的時間,這個時間其實就是 \(\sum_{i=s}^{t-1} a_i\),這裡可以預處理出 \(a_i\)的字首和,其實就是換算成下標 \(pos_i\),那麼從 \(s \to t\),就是 \(pos_t - pos_s\),其差值是 \(m\)的倍數 ,這個就意味著\(pos_t \% m == pos_s \% m\)。所以統計 \((s,t)\)的對數,其實就是統計 \(pos_i \% m\)的個數問題。
列舉 \(t\),然後考慮有多少的 \(s \leq t\)符合要求,而符合要求的 \(s\)就是滿足 \(pos_t \% m == pos_s \% m\),所以用 \(cnt\)維護\(pos_s \% m\)的個數,那麼列舉 \(t\),符合要求的 \(s\)的數量就是 \(cnt[pos_t \% m]\)。
而對於,考慮 \(s > t\)的情況 可以把環形解成鏈再翻倍一下,即\(1,2, 3, 4, 1, 2, 3, 4\),後面的\(1\)的位置就是\(pos_1 + \sum pos_i\),即偏移了一圈的距離。然後在列舉第二圈的\(t\)時,\(cnt\)維護的是第一圈的\(t+1 \sim\)第一圈的 \(n\)的 \(pos_i \% m\)的數量。
神奇的程式碼
#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, m;
cin >> n >> m;
vector<int> a(n);
for (auto& x : a)
cin >> x;
vector<int> cnt(m, 0);
LL ans = 0;
LL presum = 0;
for (int i = 0; i < n; ++i) {
ans += cnt[presum % m];
cnt[presum % m]++;
presum += a[i];
}
LL sum = presum;
presum = 0;
for (int i = 0; i < n; ++i) {
cnt[presum % m]--;
ans += cnt[sum % m];
sum += a[i];
presum += a[i];
}
cout << ans << '\n';
return 0;
}
E - Permute K times (abc367 E)
題目大意
給定陣列\(x,a\),進行 \(k\)次操作。
每次操作,求陣列 \(b_i = a_{x_i}\),然後 \(b=a\)。
問 \(k\)次操作後的陣列 \(a\)。
解題思路
題目其實可以抽象成,給定基環內向森林,點有點權。問從每個點出發,走了\(k\)步後的點的點權。
其中邊是\(i \to x_i\),點權 \(a_i\)。
由於圖是固定的,問第 \(k\)步後到達的點,預處理倍增陣列 \(run[i][j]\)表示從點 \(j\)出發走了 \(2^i\)步後到達的點。
然後對於每個點用倍增陣列求 \(k\)次後的結果即可。
神奇的程式碼
#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;
LL k;
cin >> n >> k;
vector<vector<int>> run(64, vector<int>(n));
for (int i = 0; i < n; i++) {
int v;
cin >> v;
--v;
run[0][i] = v;
}
vector<int> a(n);
for (auto& x : a)
cin >> x;
for (int i = 1; i < 64; i++) {
for (int j = 0; j < n; j++) {
run[i][j] = run[i - 1][run[i - 1][j]];
}
}
for (int i = 0; i < n; i++) {
LL cnt = k;
int cur = i;
for (int j = 0; j < 64; j++) {
if (cnt & (1LL << j)) {
cur = run[j][cur];
}
}
cout << a[cur] << " \n"[i == n - 1];
}
return 0;
}
F - Rearrange Query (abc367 F)
題目大意
給定兩個陣列\(a,b\),回答 \(q\)個問題。
每個問題給定 \(l,r,L,R\),問 \(a[l..r]\)能否透過重排,等於 \(b[L..R]\)。
解題思路
如果\(a[l..r]\)能透過重排 \(b[L..R]\),首先得 \(r-l == R - L\),然後看每個數的出現次數是否一致。顯然這判斷代價很大。
一個計算代價小的的必要條件是\(suma[l..r] == sumb[L..R]\),但不是充分條件,會有誤判的機率,會被精心構造的資料卡掉。
因為我們只要求數量相等,如果我們事先對所有數進行一個隨機對映
,這個誤判的機率將極大減小。
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
unsigned rnd() {
static unsigned A = 1 << 16 | 3, B = 33333331, C = 2341;
return C = A * C + B;
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
vector<LL> a(n);
vector<LL> b(n);
map<int, LL> tr;
for (auto& x : a) {
cin >> x;
if (tr.find(x) == tr.end()) {
tr[x] = rnd();
}
}
for (auto& x : b) {
cin >> x;
if (tr.find(x) == tr.end()) {
tr[x] = rnd();
}
}
auto presum = [&](vector<LL>& a) {
int n = a.size();
vector<LL> s1(n + 1);
for (int i = 0; i < n; ++i) {
s1[i + 1] = (s1[i] + tr[a[i]]);
}
return s1;
};
auto sa1 = presum(a);
auto sb1 = presum(b);
auto sum = [&](vector<LL>& s, int l, int r) { return (s[r] - s[l - 1]); };
auto check = [&](int l, int r, int L, int R) {
auto SA = sum(sa1, l, r);
auto SB = sum(sb1, L, R);
return SA == SB;
};
while (q--) {
int l, r, L, R;
cin >> l >> r >> L >> R;
if (r - l == R - L && check(l, r, L, R)) {
cout << "Yes" << '\n';
} else {
cout << "No" << '\n';
}
}
return 0;
}
G - Sum of (XOR^K or 0) (abc367 G)
題目大意
給定\(n\)個數的陣列\(a\),問 \(a\)所有的\(2^n-1\)個非空子序列\(b\)的價值和。
一個序列 \(b\)的價值定義如下:
- 如果個數是 \(m\)的倍數,則價值是 \((\oplus b_i)^k\)
- 否則價值是 \(0\)。
解題思路
只會\(m=1\)和 \(k=1\)。
神奇的程式碼