A-超級閃光牛可樂
#include <bits/stdc++.h>
using namespace std;
using f64 = double_t;
using i32 = int32_t;
using i64 = int64_t;
using u64 = uint64_t;
#define int long long
i32 main() {
cin.tie(nullptr)->sync_with_stdio(false);
int x, n;
cin >> x >> n;
char c, t;
int y = -1;
for (int i = 1, v; i <= n; i++) {
cin >> c >> v;
if (v > y) y = v, t = c;
}
cerr << y << "\n";
y = ( x + y - 1 ) / y ;
cerr << y << "\n";
if( y > 1000 ){
cout << "-1\n";
}else{
while( y -- ) cout << t;
cout << "\n";
}
return 0;
}
B-人列計算機
#include <bits/stdc++.h>
using namespace std;
using f64 = double_t;
using i32 = int32_t;
using i64 = int64_t;
using u64 = uint64_t;
#define int long long
i32 main() {
cin.tie(nullptr)->sync_with_stdio(false);
vector<string> s(5);
for (auto &it: s)
getline(cin, it);
int f = 0;
for (auto i: s[2]) {
if (i == '&') f = 1;
else if (i == 'O') f = 2;
}
if (f == 1) {
int x = s[1].front() == '1';
int y = s[3].front() == '1';
cout << (x & y) << "\n";
} else if (f == 2) {
int x = s[2].front() == '1';
cout << (!x) << "\n";
} else {
int x = s[1].front() == '1';
int y = s[3].front() == '1';
cout << (x | y) << "\n";
}
return 0;
}
C-時間管理大師
#include <bits/stdc++.h>
using namespace std;
using f64 = double_t;
using i32 = int32_t;
using i64 = int64_t;
using u64 = uint64_t;
#define int long long
i32 main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n;
cin >> n;
set<int> cnt;
for (int i = 1, h, m; i <= n; i++) {
cin >> h >> m;
m += h * 60;
cnt.insert(m - 1);
cnt.insert(m - 3);
cnt.insert(m - 5);
}
cout << cnt.size() << "\n";
for( auto i : cnt ){
cout << i / 60 << " " << i % 60 << "\n";
}
return 0;
}
D-我不是大富翁
二維 dp,下標為了方便可以直接轉換成\([0,n-1]\)
#include <bits/stdc++.h>
using namespace std;
using f64 = double_t;
using i32 = int32_t;
using i64 = int64_t;
using u64 = uint64_t;
#define int long long
i32 main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
cin >> n >> m;
vector<i32> f(n);
f[0] = true;
for (int i = 1, x; i <= m; i++) {
cin >> x, x %= n;
vector<i32> g(n);
for (int j = 0; j < n; j++) {
if (f[j] == false) continue;
g[(j + x) % n] |= f[j];
g[(j - x + n) % n] |= f[j];
}
f.swap(g);
}
if (f[0]) cout << "YES\n";
else cout << "NO\n";
return 0;
}
E-多重對映
反序遞推
設\(to_{i,j}\)標準最後\(i\)個操作下\(j\)會被變成\(to_{i,j}\),顯然\(to_{0,j}=j\)
然後我們設倒數第\(i\)次操作是\(x_i\)變為\(y_i\),則
\[to_{i,j}=\left\{\begin{matrix}
to_{i-1,j} & j \neq x_i\\
to_{i-1,y_i}& j = x_i
\end{matrix}\right.
\]
所以根據這個規律倒過來遞推即可,可以最佳化一維空間。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = int64_t;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
void solve() {
int n, m;
cin >> n >> m;
vector<i32> a(n);
for (auto &i: a) cin >> i;
vector<pair<i32, i32>> op(m);
for (auto &[x, y]: op) cin >> x >> y;
reverse(op.begin(), op.end());
map<i32, i32> to;
for (auto &[x, y]: op) {
if (to.count(y)) to[x] = to[y];
else to[x] = y;
}
for (auto i: a) {
if (to.count(i)) cout << to[i] << " ";
else cout << i << " ";
}
cout << "\n";
return;
}
i32 main() {
cin.tie(nullptr)->sync_with_stdio(false);
int TC;
cin >> TC;
while (TC--)
solve();
return 0;
}
啟發式合併
\(dic[x]\)表示當前被變成\(x\)的集合,則一次操作\(M(x)=y\)等價於把\(dic[x]\)中的數字全部移動到\(dic[y]\)中。
然後,啟發式合併就保證每次把較小集合插入到較大的集合中,因為在高版本的c++
中\(std::swap\)是\(O(1)\)的。
當然可以透過指標陣列來實現。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = int64_t;
using vi = vector<int>;
using pii = pair<int, int>;
const i32 N = 1e6;
vector<vi> dic(N + 1);
void solve() {
int n, m;
cin >> n >> m;
set<int> vis;
for (int i = 0, x; i < n; i++)
cin >> x, dic[x].push_back(i), vis.insert(x);
for (int x, y; m; m--) {
cin >> x >> y;
if (x == y) continue;
if (dic[x].size() > dic[y].size()) swap(dic[x], dic[y]);
dic[y].insert(dic[y].end(), dic[x].begin(), dic[x].end());
dic[x].clear();
vis.insert(y), vis.erase(x);
}
vi res(n);
for (const auto &i: vis) {
for (const auto &pos: dic[i])
res[pos] = i;
dic[i].clear();
}
for (const auto &i: res) cout << i << " ";
cout << "\n";
return;
}
i32 main() {
cin.tie(nullptr)->sync_with_stdio(false);
int TC;
cin >> TC;
while (TC--)
solve();
return 0;
}
記憶化搜尋
我們可以把操作\(M(x_i)=y_i\),看成一條有向邊\((x_i,y_i,i)\),則轉化操作就變成了在圖上每次走邊權最小的邊且所走的邊權必須是一個遞增的序列。
然後變換的過程就是沿著一條鏈走,且鏈上所經過的所有點最終都變成了鏈上的最後一個點。
所以計劃\(ans[i]\)就表示了經過\(i\)邊後最終會變成什麼,這樣可以保證每條邊只走一次,複雜度就是\(O(N+M)\)
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = int64_t;
using vi = vector<int>;
using pii = pair<int, int>;
const i32 N = 1e6;
vector<vector<pii>> e(N + 1);
vi ans(N + 1);
int dfs(int x, int time) {
if (e[x].empty() or e[x].back().second <= time) return x;
int it = -1;
for (int l = 0, r = e[x].size() - 1, mid; l <= r;) {
mid = (l + r) / 2;
if (e[x][mid].second > time) it = mid, r = mid - 1;
else l = mid + 1;
}
assert(it != -1);
auto &[y, newTime] = e[x][it];
if (ans[newTime] != -1) return ans[newTime];
return ans[newTime] = dfs(y, newTime);
}
void solve() {
int n, m;
cin >> n >> m;
vi a(n);
for (auto &i: a) cin >> i, e[i].clear();
vector<pii> op(m);
for (auto &[x, y]: op)
cin >> x >> y, e[x].clear(), e[y].clear();
for (int i = 0; i < m; i++) {
auto &[x, y] = op[i];
e[x].emplace_back(y, i + 1);
ans[i + 1] = -1;
}
for (auto i: a)
cout << dfs(i, 0) << " ";
cout << "\n";
return;
}
i32 main() {
cin.tie(nullptr)->sync_with_stdio(false);
int TC;
cin >> TC;
while (TC--)
solve();
return 0;
}