SMU Summer 2024 Contest Round 6
SMU Summer 2024 Contest Round 6
Many Formulas
题意
给你一个数,你可以在这个数的任意位之间插入零个或多个+
号,形成一个算式,你需要计算所有可能形成的算式的和。
思路
因为 \(1\le |S|\le10\),考虑暴力。
一个 n 位数,最多可以在 n-1 个空位插入加号,二进制枚举即可。
代码
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
i64 n;
cin >> n;
string s = to_string(n);
const int sz = s.size() - 1;
i64 ans = 0;
for (int i = 0; i < (1 << sz); i++) {
i64 nxt = 0;
for (int j = 0; j < s.size(); j ++) {
if (((i >> j) & 1) || j + 1 == s.size()) {
i64 res = stoll(s.substr(nxt, j - nxt + 1));
ans += res;
nxt = j + 1;
}
}
}
cout << ans << '\n';
return 0;
}
Tak and Cards
题意
给你 n 个数和一个数 A,你需要从中选出一些数使得其凑成的平均数为 A,问你这样的选数方案有多少种。
思路
n 个数选数,考虑背包。
这里还有一个问题,就是因为它是平均数,对于一个数作为平均数,它可能是 4 个数平均下来的,也可能是 3 个数平均下来的,所以我们要多开一维去记录这个数是由几个数平均下来的。
设 \(dp_{i,j}\) 为 i 个数凑成 j 的方案数。
然后就是去跑背包的时候注意为了去掉后效性要 i 个数要倒着跑,最后的答案就是 \(\sum_i^ndp_{i,i\times A}\)。
代码
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, A;
cin >> n >> A;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++)
cin >> a[i];
const int N = 2500;
vector dp(n + 1, vector<i64>(N + 1));
sort(a.begin() + 1, a.end());
dp[0][0] = 1;
for (int k = 1; k <= n; k ++)
for (int i = k; i >= 1; i --) {
for (int j = N; j >= a[k]; j --) {
dp[i][j] += dp[i - 1][j - a[k]];
}
}
i64 ans = 0;
for (int i = 1; i <= n; i ++)
ans += dp[i][i * A];
cout << ans << '\n';
return 0;
}
Wall
题意
给你 \(10\times 10\) 的一个矩阵,代表 \(0\sim 9\) 互相转化的消耗,现给你一个 \(n\times m\) 的矩阵,问你把其中不是 -1 的数全部转化为 1 的最小消耗。
思路
先对这 \(0\sim 9\) 个数跑一遍 Floyd,求出每个数转成 1 的最小消耗,然后就是累加非 -1 的数转化成 1 的消耗即可。
代码
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
const int N = 10;
int a[N][N] {};
for (int i = 0; i < 10; i ++)
for (int j = 0; j < 10; j ++)
cin >> a[i][j];
for (int k = 0; k < 10; k ++)
for (int i = 0; i < 10; i ++)
for (int j = 0; j < 10; j ++)
a[i][j] = min(a[i][j], a[i][k] + a[k][j]);
vector A(n, vector<int>(m));
i64 ans = 0;
for (auto &i : A)
for (auto &j : i) {
cin >> j;
if (j != -1) ans += a[j][1];
}
cout << ans << '\n';
return 0;
}
Coloring Edges on Tree
题意
给你一棵树,你需要给每条边染色,使得一个点所连的边中没有相同颜色的边,问你怎样给每条边染色。
思路
对于两个点,它们能互相影响的只有一条边,所以我们 dfs 跑一遍树,每次记录该点和它父节点所连边的颜色,又因为要让染色数最少,所以我们对该点的每一条边都从 1 开始染色,碰到和父节点相同的颜色就跳过即可。
代码
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<array<int, 2>> edge(n);
map<pair<int, int>, int> ans;
vector g(n + 1, vector<int>());
for (int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
edge[i] = {u, v};
g[u].push_back(v);
g[v].push_back(u);
}
set<int> Col;
auto dfs = [&](auto & self, int u, int fa, int x)->void{
int color = 1;
for (auto v : g[u]) {
if (v == fa)continue;
if (color == x)color++;
ans[ {u, v}] = ans[ {v, u}] = color;
Col.insert(color);
self(self, v, u, color);
color++;
}
};
dfs(dfs, 1, 0, 0);
cout << Col.size() << '\n';
for (int i = 1; i < n; i ++) {
auto [u, v] = edge[i];
cout << ans[ {u, v}] << '\n';
}
return 0;
}
Fault-tolerant Network
题意
现有 \(2×𝑛\) 台计算机分成两排,每排有 N 台,每台都有一定的价值,并且相邻的两台计算机之间通过网线连接。
现在请你在这两排之间接通一些网线,使得任意一台计算机坏掉后这些计算机还是连通的。
连接两台计算机的代价为 \(∣𝑎−𝑏∣\),\(𝑎,𝑏\) 分别表示这两台计算机的价值。
求出连通两排电脑的最小代价。
思路
手玩一些例子可以发现,对于 \(a_1,a_n,b_1,b_n\) 这四个点是必须要连着边的,因为对于这四个点中的哪个点,如果没有边与它相连,那么总是可以通过断掉它旁边的点来使它孤立。
那我们就从这四个点出发分类讨论:
- 考虑连两条边的情况,就是 a 的两个点与 b 的两个点两两互连。
- 考虑连三条边的情况,就是 a 的两个点与 b 的两个点只连其中一对,另外两个点连接 a 中或 b 中代价最小的边。
- 考虑连四条边的情况,就是四个点都连对面最小代价的边。
代码
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
vector<i64> a(n + 1), b(n + 1);
for (int i = 1; i <= n; i ++)
cin >> a[i];
for (int i = 1; i <= n; i ++)
cin >> b[i];
i64 ans = min(abs(a[1] - b[1]) + abs(a[n] - b[n]), abs(a[1] - b[n]) + abs(a[n] - b[1]));
i64 a1, an, b1, bn;
a1 = an = b1 = bn = INT_MAX;
for (int i = 1; i <= n; i ++) {
a1 = min(a1, abs(a[1] - b[i]));
an = min(an, abs(a[n] - b[i]));
b1 = min(b1, abs(b[1] - a[i]));
bn = min(bn, abs(b[n] - a[i]));
}
ans = min({ans, abs(a[1] - b[1]) + an + bn, abs(a[1] - b[n]) + an + b1, abs(a[n] - b[1]) + a1 + bn, abs(a[n] - b[n]) + a1 + b1});
ans = min(ans, a1 + an + b1 + bn);
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
Nearest Excluded Points
题意
给你一堆标记的点,你需要从坐标轴上找到非标记的点中离这些点曼哈顿距离最近的点。
思路
对于一个点,其离它曼哈顿距离最近的点肯定是它周围的四个点。
那么当一堆点合成一个块的时候,那么离这些点最近的曼哈顿距离最近的点也一定是这个块的周围的点,那我们可以用 bfs 将周围的这些非标记点作为起点去更新块中每层点的最近答案,初始非标记点的答案要更新为自己。
代码
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<pair<int, int>> loc(n);
const int u[] = {1, -1, 0, 0}, v[] = {0, 0, 1, -1};
map<pair<int, int>, pair<int, int>> ans;
set<pair<int, int>> st;
queue<pair<int, int>> Q;
for (auto &[x, y] : loc) {
cin >> x >> y;
st.insert({x, y});
}
for (auto [x, y] : loc) {
for (int i = 0; i < 4; i ++) {
int dx = x + u[i], dy = y + v[i];
if (st.count({dx, dy})) continue;
Q.push({dx, dy});
ans[ {dx, dy}] = {dx, dy};
}
}
while (Q.size()) {
auto [x, y] = Q.front();
Q.pop();
for (int i = 0; i < 4; i ++) {
int dx = x + u[i], dy = y + v[i];
if (st.count({dx, dy})) {
ans[ {dx, dy}] = ans[ {x, y}];
Q.push({dx, dy});
st.erase({dx, dy});
}
}
}
for (auto node : loc) {
auto [x, y] = ans[node];
cout << x << ' ' << y << '\n';
}
return 0;
}
Vacation Query
题意
长度为 \(N\) 的 01 序列 \(S\),进行 \(Q\) 次操作:
1 L R
:将 \(S_L\) 到 \(S_R\) 每一位取反。2 L R
:求 \(S_L\) 到 \(S_R\) 的最长连续 1 串的长度。
思路
线段树维护最大子段和的模板题。
推荐题目:P2572 [SCOI2010] 序列操作 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
代码
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define lc u << 1
#define rc u << 1 | 1
const int N = 1e5 + 5;
i64 w[N], n, m, p;
struct Tree { //线段树
int l, r;
int sum1, lmax1, rmax1, max1;
int sum0, lmax0, rmax0, max0;
int tag, rev;
} tr[N << 2];
void cal_lazy(int fa, int ch) {
int len = tr[ch].r - tr[ch].l + 1;
if (tr[fa].tag == 0) {
tr[ch].tag = 0, tr[ch].rev = 0;
tr[ch].sum0 = tr[ch].lmax0 = tr[ch].rmax0 = tr[ch].max0 = len;
tr[ch].sum1 = tr[ch].lmax1 = tr[ch].rmax1 = tr[ch].max1 = 0;
}
if (tr[fa].tag == 1) {
tr[ch].tag = 1, tr[ch].rev = 0;
tr[ch].sum0 = tr[ch].lmax0 = tr[ch].rmax0 = tr[ch].max0 = 0;
tr[ch].sum1 = tr[ch].lmax1 = tr[ch].rmax1 = tr[ch].max1 = len;
}
if (tr[fa].rev) {
tr[ch].rev ^= 1;
swap(tr[ch].sum1, tr[ch].sum0);
swap(tr[ch].lmax1, tr[ch].lmax0);
swap(tr[ch].rmax1, tr[ch].rmax0);
swap(tr[ch].max1, tr[ch].max0);
}
}
void tag_union(int fa, int ch) {
tr[ch].tag = tr[fa].tag;
tr[ch].rev ^= tr[fa].rev;
}
void init_lazy(int u) {
tr[u].tag = -1;
tr[u].rev = 0;
}
void pushdown(int u) {
if (tr[u].tag != -1 || tr[u].rev != 0) {
cal_lazy(u, lc);
cal_lazy(u, rc);
// tag_union(u, lc);
// tag_union(u, rc);
init_lazy(u);
}
}
void pushup(int u) { //上传
tr[u].sum1 = tr[lc].sum1 + tr[rc].sum1;
tr[u].sum0 = tr[lc].sum0 + tr[rc].sum0;
tr[u].lmax1 = tr[lc].lmax1 + (tr[lc].sum0 ? 0 : tr[rc].lmax1);
tr[u].rmax1 = tr[rc].rmax1 + (tr[rc].sum0 ? 0 : tr[lc].rmax1);
tr[u].lmax0 = tr[lc].lmax0 + (tr[lc].sum1 ? 0 : tr[rc].lmax0);
tr[u].rmax0 = tr[rc].rmax0 + (tr[rc].sum1 ? 0 : tr[lc].rmax0);
tr[u].max1 = max({tr[lc].max1, tr[rc].max1, tr[lc].rmax1 + tr[rc].lmax1});
tr[u].max0 = max({tr[lc].max0, tr[rc].max0, tr[lc].rmax0 + tr[rc].lmax0});
}
void build(int u, int l, int r) { //建树
tr[u].l = l, tr[u].r = r;
init_lazy(u);
if (l == r) {
int t = w[l];
tr[u] = {l, r, t, t, t, t, t ^ 1, t ^ 1, t ^ 1, t ^ 1, -1, 0};
return ;
}
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int op) {
if (tr[u].l >= l && tr[u].r <= r) {
int len = tr[u].r - tr[u].l + 1;
if (op == 0) {
tr[u].rev = 0, tr[u].tag = 0;
tr[u].sum0 = tr[u].lmax0 = tr[u].rmax0 = tr[u].max0 = len;
tr[u].sum1 = tr[u].lmax1 = tr[u].rmax1 = tr[u].max1 = 0;
} else if (op == 1) {
tr[u].rev = 0, tr[u].tag = 1;
tr[u].sum0 = tr[u].lmax0 = tr[u].rmax0 = tr[u].max0 = 0;
tr[u].sum1 = tr[u].lmax1 = tr[u].rmax1 = tr[u].max1 = len;
} else {
tr[u].rev ^= 1;
swap(tr[u].sum1, tr[u].sum0);
swap(tr[u].lmax1, tr[u].lmax0);
swap(tr[u].rmax1, tr[u].rmax0);
swap(tr[u].max1, tr[u].max0);
}
return ;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid)
modify(lc, l, r, op);
if (r > mid)
modify(rc, l, r, op);
pushup(u);
}
Tree query(int u, int l, int r) { //区查
if (l <= tr[u].l && tr[u].r <= r)
return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if (r <= mid) return query(lc, l, r);
else if (l > mid) return query(rc, l, r);
else {
Tree res, L = query(lc, l, mid), R = query(rc, mid + 1, r);
res.sum1 = L.sum1 + R.sum1;
res.sum0 = L.sum0 + R.sum0;
res.lmax1 = L.lmax1 + (L.sum0 ? 0 : R.lmax1);
res.rmax1 = R.rmax1 + (R.sum0 ? 0 : L.rmax1);
res.lmax0 = L.lmax0 + (L.sum1 ? 0 : R.lmax0);
res.rmax0 = R.rmax0 + (R.sum1 ? 0 : L.rmax0);
res.max1 = max({L.max1, R.max1, L.rmax1 + R.lmax1});
res.max0 = max({L.max0, R.max0, L.rmax0 + R.lmax0});
return res;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
string s;
cin >> s;
for (int i = 1; i <= n; i ++)
w[i] = s[i - 1] - '0';
build(1, 1, n);
while (m--) {
int op, l, r;
cin >> op >> l >> r;
if (op == 1) {
modify(1, l, r, 2);
} else {
auto ans = query(1, l, r);
cout << ans.max1 << '\n';
}
}
return 0;
}