T1:树的计数

注意到,深度为 \(2\) 的点一定是深度为 \(1\) 的点的儿子节点,深度为 \(3\) 的点一定是深度为 \(2\) 的点的儿子节点.....那么深度为 \(i\) 的点可以是深度为 \(i - 1\) 的儿子节点,对于此题是一个经典的分步乘法计数原理,把深度为 \(2\) 的儿子节点确定下来是第一步,深度为 \(3\) 的儿子节点确定下来是第二步......

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

//const int mod = 998244353;
const int mod = 1000000007;
struct mint {
    ll x;
    mint(ll x=0):x((x%mod+mod)%mod) {}
    mint operator-() const {
        return mint(-x);
    }
    mint& operator+=(const mint a) {
        if ((x += a.x) >= mod) x -= mod;
        return *this;
    }
    mint& operator-=(const mint a) {
        if ((x += mod-a.x) >= mod) x -= mod;
        return *this;
    }
    mint& operator*=(const mint a) {
        (x *= a.x) %= mod;
        return *this;
    }
    mint operator+(const mint a) const {
        return mint(*this) += a;
    }
    mint operator-(const mint a) const {
        return mint(*this) -= a;
    }
    mint operator*(const mint a) const {
        return mint(*this) *= a;
    }
    mint pow(ll t) const {
        if (!t) return 1;
        mint a = pow(t>>1);
        a *= a;
        if (t&1) a *= *this;
        return a;
    }

    // for prime mod
    mint inv() const {
        return pow(mod-2);
    }
    mint& operator/=(const mint a) {
        return *this *= a.inv();
    }
    mint operator/(const mint a) const {
        return mint(*this) /= a;
    }
};
istream& operator>>(istream& is, mint& a) {
    return is >> a.x;
}
ostream& operator<<(ostream& os, const mint& a) {
    return os << a.x;
}

int main() {
    int n;
    cin >> n;
    
    map<int, int> mp;
    rep(i, n) {
        int d;
        cin >> d;
        mp[d]++;
    }
    
    mint ans = 1;
    for (auto [d, x] : mp) {
        if (d == 1) continue;
        ans *= mint(mp[d-1]).pow(x);
    }
    
    cout << ans << '\n';
    
    return 0;
}

T2:套娃

本质上就是 LC354. 俄罗斯套娃信封问题,唯一的不同点在于 不超过

代码实现
#include <bits/stdc++.h>
#define int long long
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using P = pair<int, int>;

signed main() {
    int n;
    cin >> n;
    
    vector<P> ps;
    rep(i, n) {
        int a, b;
        cin >> a >> b;
        ps.emplace_back(a, b);
    }
    
    sort(ps.begin(), ps.end());
    
    vector<int> dp{ps[0].second};
    for (int i = 1; i < n; ++i) {
        if (int x = ps[i].second; x >= dp.back()) {
            dp.push_back(x);
        }
        else {
            auto it = upper_bound(dp.begin(), dp.end(), x);
            *it = x;
        }
    }

    cout << dp.size() << '\n';
    
    return 0;
}

T3:订单安排

对于当前一些订单已经延迟的人,肯定是希望把不悦值最大的人先完成,每次完成需要一分种,也就是说每一次完成一个人的订单后会有一个新的人订单延迟,那么将此新人加入进来后需要重新求不悦值最大的人,使用大根堆可以在 \(\mathcal{O}(\log n)\) 的时间内完成此操作。

双倍经验:CF853A

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> a(n);
    rep(i, n) cin >> a[i];
    
    ll ans = 0, tot = 0;
    priority_queue<int> q;
    rep(i, n+m) {
        if (i < n) {
            q.push(a[i]);
            tot += a[i];
        } 
        if (i >= m) {
            int x = q.top(); q.pop();
            tot -= x;
        }
        ans += tot;
    }
    
    cout << ans << '\n';
    
    return 0;
}

T4:积木染色(三)

区间 dp

本质上就是 P4170 [CQOI2007] 涂色,将染色过程倒过来做一遍,但是要对结果减去 \(1\),因为要求最终所有的积木同色,而不是无色

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)

using namespace std;

int dp[505][505];

int main() {
    int n;
    cin >> n;
    
    vector<int> a(n+1);
    rep(i, n) cin >> a[i];
    
	memset(dp, 0x3f, sizeof dp);
	rep(i, n) dp[i][i] = 1;
	
	for (int len = 2; len <= n; ++len) {
		for (int i = 1; i + len - 1 <= n; ++i) {
			int j = i + len - 1;
			if (a[i] == a[j]) dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]);
			else {
				for (int k = i; k < j; ++k) {
					dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
				}
			}
		}
	}
	
	cout << dp[1][n]-1 << '\n';
	
	return 0;
}