T1:Full Moon

模拟

代码实现
n, m, p = map(int, input().split())
ans = 0
i = m
while i <= n:
ans += 1
i += p
print(ans)

或者答案是 n+(pm)p

T2:Overlapping sheets

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
int m = 100;
vector s(m, vector<int>(m));
rep(_, n) {
int a, b, c, d;
cin >> a >> b >> c >> d;
for (int i = a; i < b; ++i) {
for (int j = c; j < d; ++j) {
s[i][j] = 1;
}
}
}
int ans = 0;
rep(i, m)rep(j, m) if (s[i][j] == 1) ans++;
cout << ans << '\n';
return 0;
}

T3:Blue Spring

贪心

  • 先将 f 做升序排序
  • 预处理一下 f 的前缀和
  • 可以枚举后缀哪几个 d 天买一日游券,剩下的前缀就都是买常规票
代码实现
#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, d, p;
cin >> n >> d >> p;
vector<int> f(n);
rep(i, n) cin >> f[i];
sort(f.begin(), f.end());
ll now = 0;
rep(i, n) now += f[i];
ll ans = now;
while (f.size()) {
ll s = 0;
int sz = min<int>(d, f.size());
rep(i, sz) {
s += f.back();
f.pop_back();
}
now -= s; now += p;
ans = min(ans, now);
}
cout << ans << '\n';
return 0;
}

T4:General Weighted Max Matching

注意到由于边权是按字典序给出的,所以只需要在边列表上做 dfs 暴力枚举出所有可能的合法匹配即可

代码实现
#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;
cin >> n;
vector d(n+1, vector<int>(n+1));
rep(i, n) {
for (int j = i+1; j < n; ++j) {
cin >> d[i][j];
d[j][i] = d[i][j];
}
}
if (n&1) n++;
ll ans = 0;
vector<bool> used(n);
auto f = [&](auto f, ll w) -> void {
ans = max(ans, w);
int i = 0;
while (i < n and used[i]) i++;
if (i == n) return;
used[i] = true;
for (int j = i+1; j < n; ++j) {
if (used[j]) continue;
used[j] = true;
f(f, w+d[i][j]);
used[j] = false;
}
used[i] = false;
};
f(f, 0);
cout << ans << '\n';
return 0;
}

T5:Sandwiches

容斥,无视条件 3 的方案数减去满足 Ai=Aj=Ak 的方案数

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
ll c3(ll n) { // nC3
return n*(n-1)*(n-2)/6;
}
int main() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
vector<int> cnt(n+1);
vector<ll> sum(n+1);
ll ans = 0;
rep(k, n) {
ans += ll(k-1)*cnt[a[k]] - sum[a[k]];
cnt[a[k]]++;
sum[a[k]] += k;
}
rep(i, n) ans -= c3(cnt[i+1]);
cout << ans << '\n';
return 0;
}

T6:Octopus

考虑枚举 k,那么我们可以在 O(NlogN) 的时间里判断是否满足条件:将所有的 |Xik| 排序,得到序列 di,判断是否所有 diLi 即可。

注意到我们只关心选择的 k 是不是能让第 j 个机械臂取到 Xi,那么我们把所有的 XiLj1Xi+Lj 作为关键点,则任意两个关键点之间(左开右闭)的数本质都是一样的 ,要么同时满足,要么同时不满足。

于是要判断的数的个数变成了 N2 ,总时间复杂度 O(N3logN)

代码实现
#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;
cin >> n;
vector<ll> x(n), l(n);
rep(i, n) cin >> x[i];
rep(i, n) cin >> l[i];
sort(l.begin(), l.end());
set<ll> ps;
rep(i, n)rep(j, n) {
ps.insert(x[i]-l[j]-1);
ps.insert(x[i]+l[j]);
}
ll ans = 0;
ll pre = 0;
for (ll p : ps) {
bool ok = true;
vector<ll> d(n);
rep(i, n) d[i] = abs(x[i]-p);
sort(d.begin(), d.end());
rep(i, n) if (d[i] > l[i]) ok = false;
if (ok) ans += p-pre;
pre = p;
}
cout << ans << '\n';
return 0;
}

T7:Typical Path Problem

注意到,是否存在一条从点 A 经过点 B 到达点 C 的简单路径可以被拆解成是否存在一条从点 B 到达点 A 的简单路径以及一条从点 B 到达点 C 的简单路径

为了保证每个点只经过一次,所以需要拆点,点 V 作为流入点,还需增加一个流出点 V。具体地,对每个点 V 到点 V 连一条容量为 1 的边。

对于原图上的每条边 (U,V), 需要对点 U 到点 V 连一条容量为 1 的边,同时还需对点 V 到点 U 连一条容量为 1 的边

还需建立一个汇点 T,对点 A,C 到点 T 连一条容量为 1 的边

最后只需从源点 B 到汇点 T 跑一遍最大流验证结果是否是 2 即可。

代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int a, b, c;
cin >> a >> b >> c;
--a; --b; --c;
int tv = n+n;
mf_graph<int> g(tv+1);
rep(i, n) g.add_edge(i, n+i, 1);
rep(i, m) {
int u, v;
cin >> u >> v;
--u; --v;
g.add_edge(n+u, v, 1);
g.add_edge(n+v, u, 1);
}
g.add_edge(n+a, tv, 1);
g.add_edge(n+c, tv, 1);
if (g.flow(n+b, tv) == 2) puts("Yes");
else puts("No");
return 0;
}