T1:录制节目

可以将原题转化成

\(n\) 条线段,可以保留若干条线段,并且可以分成两部分,使得每部分的线段互不相交

先将所有线段按右端点做升序排序,且按左端点做降序排序
然后维护两个变量 last1last2
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;
}