牛客小白月赛88
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;
}