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;
}