T1:录制节目
可以将原题转化成
有 \(n\) 条线段,可以保留若干条线段,并且可以分成两部分,使得每部分的线段互不相交
先将所有线段按右端点做升序排序,且按左端点做降序排序
然后维护两个变量 last1
和 last2
last1
:第一个部分的最后的端点
last2
:第二个部分的最后的端点
尽量让 \(\min(\operatorname{last1}, \operatorname{last2})\) 更小
原题:P2255
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
struct line {
int l, r;
bool operator<(const line& o) const {
if (r == o.r) return l > o.l;
return r < o.r;
};
};
int main() {
int n;
cin >> n;
vector<line> d(n);
rep(i, n) cin >> d[i].l >> d[i].r;
sort(d.begin(), d.end());
int ans = 0;
int last1 = 0, last2 = 0;
for (auto [l, r] : d) {
if (last1 < last2) swap(last1, last2);
if (l >= last1) {
ans++;
last1 = r;
}
else if (l >= last2) {
ans++;
last2 = r;
}
}
cout << ans << '\n';
return 0;
}
T2:算式求值(二)
原题:基本计算器
代码实现
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
string s;
cin >> s;
int n = s.size();
ll ans = 0;
int sign = 1;
stack<int> ops;
ops.push(1);
int i = 0;
while (i < n) {
if (s[i] == '+') {
sign = ops.top();
i++;
}
else if (s[i] == '-') {
sign = -ops.top();
i++;
}
else if (s[i] == '(') {
ops.push(sign);
i++;
}
else if (s[i] == ')') {
ops.pop();
i++;
}
else {
ll num = 0;
while (i < n and isdigit(s[i])) {
num = num*10 + s[i]-'0';
i++;
}
ans += sign * num;
}
}
cout << ans << '\n';
return 0;
}
T3:田忌赛马
先将 \(a\) 和 \(b\) 做升序排序
然后进行分类讨论:
- 如果 \(a\) 剩下的最小值大于 \(b\) 剩下的最小值,则用 \(a\) 剩下的最小值来打 \(b\) 剩下的最小值
- 如果 \(a\) 剩下的最大值大于 \(b\) 剩下的最大值,则用 \(a\) 剩下的最大值来大 \(b\) 剩下的最大值
- 否则用 \(a\) 最小的去消耗 \(b\) 最大的
原题:P1650
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n), b(n);
rep(i, n) cin >> a[i];
rep(i, n) cin >> b[i];
sort(a.begin(), a.end());
sort(b.begin(), b.end());
int ans = 0;
int l1 = 0, r1 = n-1;
int l2 = 0, r2 = n-1;
while (l1 <= r1 and l2 <= r2) {
if (a[l1] > b[l2]) {
ans++;
l1++; l2++;
}
else if (a[r1] > b[r2]) {
ans++;
r1--; r2--;
}
else {
if (a[l1] < b[r2]) ans--;
l1++; r2--;
}
}
cout << ans << '\n';
return 0;
}
T4:树的颜色
有三种做法:
- 树上启发式合并
- 树上莫队
- 可以用树上 \(\operatorname{dfs}\) 来统计区间 \(\bigg[\operatorname{in}[1], \operatorname{out}[v]\bigg]\) 中每个点对 点 \(v\) 上颜色 \(c\) 的贡献,然后将它容斥掉区间 \(\bigg[\operatorname{in}[1], \operatorname{in}[v]\bigg]\) 中的点对颜色 \(c\) 的贡献,就能求出点 \(v\) 的答案。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<vector<int>> to(n);
for (int i = 1; i < n; ++i) {
int p;
cin >> p;
--p;
to[p].push_back(i);
}
vector<int> c(n);
rep(i, n) cin >> c[i], c[i]--;
vector<int> cnt(n), ans(n);
auto dfs = [&](auto& f, int v) -> void {
ans[v] = ++cnt[c[v]];
for (int u : to[v]) {
f(f, u);
}
ans[v] = cnt[c[v]]-ans[v];
};
dfs(dfs, 0);
rep(i, n) cout << ans[i] << ' ';
return 0;
}