2024牛客寒假算法基础集训营2
1|0A-Tokitsukaze and Bracelet
分类讨论
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
for (int a, b, c, res; n; n--) {
cin >> a >> b >> c, res = 0;
res += (a - 100) / 50;
if (34 <= b and b <= 40) res += 1;
else if (b == 45) res += 2;
if (34 <= c and c <= 40) res += 1;
else if (c == 45) res += 2;
cout << res << "\n";
}
return 0;
}
2|0B-Tokitsukaze and Cats
其实就是特判每只周围有没有猫即可
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k;
vector e(n + 1, vi(m + 1));
for (int i = 1, x, y; i <= k; i++)
cin >> x >> y, e[x][y] = 1;
int res = k * 4;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (e[i][j] != 1) continue;
if (i + 1 <= n and e[i + 1][j]) res--;
if (j + 1 <= m and e[i][j + 1]) res--;
}
cout << res << "\n";
return 0;
}
3|0C-Tokitsukaze and Min-Max XOR
因为是求子序列,所以可以对a进行排序。
排序后如果选择了[l,r],则min=al,max=ar,而中间的数可以随便选,所以贡献就是2r−l−1
这样枚举端点的话复杂度是O(n2),我们先考虑2r−l−1=2r−12l,因此对于一个复合条件的r所有合法的l贡献和为2r−1⋅∑12l,所以我们可以枚举r然后快速的求和
我们可以考虑用 01Tire来维护这个信息,首先就是把所有的二进制串对其,然后后对于每插入一个串,其在路径上的贡献就是12i。
首先x⊕y≤k,要满足这样的条件x⊕y一定存在一个前缀等于k,然后下一位k取1,x⊕y取0,再之后的位就可以随便取了。
因此我们从根开始查找,如果这一位k是1,那么当前位可以异或和0则当前节点子树中的所有串均符合条件,然后向着让当前位异或后和k相同的方向走即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
const int mod = 1e9 + 7;
struct Node {
int sum = 0;
array<Node *, 2> ch;
Node() {
ch.fill(nullptr);
}
};
const int N = 2e5;
vi pw(N), inv(N);
void solve() {
int n, k;
cin >> n >> k;
vector<int> a(n);
for (auto &i: a) cin >> i;
sort(a.begin(), a.end());
Node *root = new Node();
int res = 0;
for (int i = 0; i < n; i++) {
auto p = root;
int ans = 0;
for (int j = 29, w, v; j >= 0 and p != nullptr; j--) {
w = (a[i] >> j) & 1, v = (k >> j) & 1;
if (v == 1) {
if (p->ch[w] != nullptr) ans += p->ch[w]->sum;
p = p->ch[w ^ 1];
} else {
p = p->ch[w];
}
}
if (p != nullptr) ans += p->sum;
res = (res + 1 + ans % mod * pw[i - 1] % mod) % mod;
p = root;
for (int j = 29, w; j >= 0; j--) {
w = (a[i] >> j) & 1;
if (p->ch[w] == nullptr) p->ch[w] = new Node();
p = p->ch[w];
p->sum = (p->sum + inv[i]) % mod;
}
}
cout << res % mod << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
pw[0] = inv[0] = 1;
for (int i = 1; i < N; i++)
pw[i] = pw[i - 1] * 2 % mod, inv[i] = inv[i - 1] * (mod + 1) / 2 % mod;
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
4|0D-Tokitsukaze and Slash Draw
每一个操作可以无限次的使用,这样的话就可以当成一个完全背包来做。f[i]表示把一击必杀移动i的最小花费
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = 1e18;
const int mod = 998244353;
void solve() {
int n, m, k;
cin >> n >> m >> k;
vi f(n, INF);
f[0] = 0;
for (int i = 0, a, b, flag; i < m; i++) {
cin >> a >> b;
do {
flag = 0;
for (int j = 0, t; j < n; j++) {
if (f[j] == INF) continue;
t = (j + a) % n;
if (f[j] + b < f[t])
f[t] = f[j] + b, flag = 1;
}
} while (flag);
}
int res = f[n-k];
if( res == INF ) res = -1;
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
5|0E-Tokitsukaze and Eliminate (easy)
其实只要动态维护每个颜色最靠右的位置,以及当前所有最靠右的最小值,然后贪心即可
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
void solve() {
int n;
cin >> n;
vector<vi> cnt(2);
for (int i = 1, x; i <= n; i++)
cin >> x, x--, cnt[x].push_back(i);
int res = 0;
while (not cnt[0].empty() and not cnt[1].empty()) {
res++;
int i = 0;
if (cnt[1].back() < cnt[0].back()) i = 1;
while (not cnt[i ^ 1].empty() and cnt[i ^ 1].back() > cnt[i].back()) cnt[i ^ 1].pop_back();
cnt[i].pop_back();
}
res += cnt[0].size() + cnt[1].size();
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
6|0F-Tokitsukaze and Eliminate (hard)
这里颜色很多。我们看删除什么样的后缀最优呢?就是恰好包含所有种类的颜色是最优的,因为在往前走一定是重复的,因此必定无法通过一次操作删除。知道这个结论后只要倒序遍历一遍即可。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
void solve() {
int n;
cin >> n;
vi a(n), b(n);
int cnt = 0, t = 0;
for (auto &i: a)
cin >> i, i--, b[i]++, cnt += b[i] == 1;
reverse(a.begin(), a.end());
set<int> s;
int res = 0;
for (const auto &i: a) {
b[i]--;
if (b[i] == 0) t++;
s.insert(i);
if (s.size() == cnt)
res++, s.clear(), cnt -= t, t = 0;
}
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
7|0I-Tokitsukaze and Short Path (plus)
容易推得绕路一定不优,所以就是所有边之和就是答案,因此第一个绝对值就能去掉,只剩下第二个绝对值。我们排序,然后维护前缀和后缀和即可。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
void solve() {
int n;
cin >> n;
vi a(n);
int res = 0, sum = 0;
for (auto &i: a) cin >> i, sum += i;
res += sum * (n - 1) *2;
sort(a.begin(), a.end());
for (int i = 0, x = 0, y = n, pre = 0, suf = sum; i < n; i++) {
res += (x * a[i] - pre) + (suf - y * a[i]);
x++, y--, pre += a[i], suf -= a[i];
}
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
8|0J-Tokitsukaze and Short Path (minus)
通过简单的分类讨论就可以推得wu,v=2min(au,av)。因此最短路有两种情况,一种是直接走,另一种是绕路的最小值。
因此我们可以把所有的点排序,然后考虑从当前点出发是直接走短还是绕路短,并且计算出来回的方案数即可
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
void solve() {
int n;
cin >> n;
vi a(n);
int res = 0;
for (auto &i: a) cin >> i;
sort(a.begin(), a.end());
int d = a.front(), t = n;
for (auto &i: a) {
t--;
res += min(4 * d, i * 2) * t * 2;
}
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
9|0K-Tokitsukaze and Password (easy)
判断一个数是不是8的倍数,只需要判断最低三位即可。
所以可以先爆搜出8的倍数,然后再爆搜其他位置即可
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = 1e18;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
int n, y, res;
set<int> vis;
void dfs(string s, i32 i) {
if (i == 3) {
int x = 0;
for (int j = 0, t = 1; j < 3; j++, t *= 10)
x += (s[j] - '0') * t;
if (x % 8 != 0) return;
}
if (i == n) {
if (s.back() == '0' and n > 1) return;
int x = 0;
for (int j = 0, t = 1; j < n; j++, t *= 10)
x += (s[j] - '0') * t;
if (x % 8 != 0 or x > y) return;
res++;
return;
}
if (s[i] >= '0' and s[i] <= '9') {
dfs(s, i + 1);
return;
} else if (s[i] == '_') {
for (s[i] = '0'; s[i] <= '9'; s[i]++)
dfs(s, i + 1);
s[i] = '_';
return;
} else {
for (int j = '0'; j <= '9'; j++) {
if (vis.count(j)) continue;
vis.insert(j);
auto t = s;
for (auto &l: t)
if (l == s[i]) l = j;
dfs(t, i + 1);
vis.erase(j);
}
}
}
void solve() {
string s;
cin >> n >> s >> y, res = 0;
reverse(s.begin(), s.end()), vis.clear();
dfs(s, 0);
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
__EOF__

本文链接:https://www.cnblogs.com/PHarr/p/18057127.html
关于博主:前OIer,SMUer
版权声明:CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现