T1:Nine

A+1=BA%3=1 时,说明 AB 相邻

代码实现
a, b = map(int, input().split())
if a+1 == b and a%3 != 0:
print('Yes')
else:
print('No')

T2:Rotate

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int main() {
int n;
cin >> n;
vector<string> a(n);
rep(i, n) cin >> a[i];
vector<P> ps;
rep(i, n-1) ps.emplace_back(0, i);
rep(i, n-1) ps.emplace_back(i, n-1);
rep(i, n-1) ps.emplace_back(n-1, n-1-i);
rep(i, n-1) ps.emplace_back(n-1-i, 0);
auto b = a;
int m = ps.size();
rep(mi, m) {
auto [i, j] = ps[mi];
auto [ni, nj] = ps[(mi+1)%m];
b[ni][nj] = a[i][j];
}
rep(i, n) cout << b[i] << '\n';
return 0;
}

T3:Medicine

对于第 i 天所吃的药的总和为 si,我们可以将相同的 si 划分为一组

比如样例1:

1: 19
2: 10
3: 5
4: 5
5: 3
6: 3
7: 0

可以将 5,5 划分为一组,3,3 划分到一组,其他数单独为一组

考虑对序列 (ai,si)ai 的大小进行升序排序
可以发现,如果 si+1si,那么 si+1 一定在新的组中,我们只需考虑同一个 si 第一次出现的位置即可
于是,我们可以仅对序列 (ai,bi)ai 的大小进行升序排序
在我们扫描序列 (ai,bi) 时,可以通过当前组的 s 减去 bi 就能得到新的一组的 s

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int, int>;
int main() {
int n, k;
cin >> n >> k;
vector<P> ps;
ll tot = 0;
rep(i, n) {
int a, b;
cin >> a >> b;
ps.emplace_back(a, b);
tot += b;
}
if (tot <= k) {
puts("1");
return 0;
}
sort(ps.begin(), ps.end());
for (auto [a, b] : ps) {
tot -= b;
if (tot <= k) {
cout << a+1 << '\n';
return 0;
}
}
return 0;
}

也可以用 std::map<int, int> 来实现

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int, int>;
int main() {
int n, k;
cin >> n >> k;
map<int, ll> mp;
ll tot = 0;
rep(i, n) {
int a, b;
cin >> a >> b;
mp[a] += b;
tot += b;
}
if (tot <= k) {
puts("1");
return 0;
}
for (auto [a, b] : mp) {
tot -= b;
if (tot <= k) {
cout << a+1 << '\n';
return 0;
}
}
return 0;
}

T4:Add One Edge

跑两遍 bfs 分别求出从点 1 和点 N1+N2 开始的最长路

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int, int>;
int main() {
int n1, n2, m;
cin >> n1 >> n2 >> m;
int n = n1+n2;
vector<vector<int>> to(n);
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
to[a].push_back(b);
to[b].push_back(a);
}
vector<int> d1, dt;
auto bfs = [&](int sv) {
const int INF = 1001001001;
vector<int> dist(n, INF);
queue<int> q;
q.push(sv); dist[sv] = 0;
while (q.size()) {
int v = q.front(); q.pop();
for (int u : to[v]) {
if (dist[u] != INF) continue;
dist[u] = dist[v]+1;
q.push(u);
}
}
return dist;
};
d1 = bfs(0);
dt = bfs(n-1);
int ans = 1;
{
int mx = 0;
rep(i, n1) mx = max(mx, d1[i]);
ans += mx;
}
{
int mx = 0;
rep(i, n2) mx = max(mx, dt[n1+i]);
ans += mx;
}
cout << ans << '\n';
return 0;
}

T5:Family and Insurance

注意到,假设点 i 的父节点为 x,同时 x 的保险补偿对象是他的子孙 y 代人,那么点 i 除了保险对象以外,还需将保险补偿交给他的子孙 y1 代人,所以要将 y1 挂在点 i

dp[v] 表示点 v 的保险补偿对象是他的后几代人
然后自顶向下转移即可

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> to(n);
for (int i = 1; i < n; ++i) {
int p;
cin >> p;
--p;
to[p].push_back(i);
}
vector<int> d(n, -1);
rep(i, m) {
int x, y;
cin >> x >> y;
--x;
d[x] = max(d[x], y);
}
auto dfs = [&](auto f, int v) -> void {
for (int u : to[v]) {
d[u] = max(d[u], d[v]-1);
f(f, u);
}
};
dfs(dfs, 0);
int ans = 0;
rep(i, n) if (d[i] != -1) ans++;
cout << ans << '\n';
return 0;
}

T6:Box in Box

首先,我们可以对 (hi,wi,di) 排序使得 hi<wi<di
然后接下来就是扫描线的工作了

具体地,我们可以先将 (hi,wi,di)hi 的大小做升序排序,再枚举 (hi,wi,di),调查之前有没有 (hj,wj,dj) 满足 wj<widj<di,此时我们可以不管 hi,那么这个问题就是个二维偏序问题:我们需要对 wi 进行离散化并作为 x 坐标,将 di 作为 y 坐标,然后调查当前 x 坐标左边的 y 坐标的最小值是否小于当前的 y 坐标即可,可以用线段树来维护

代码实现
#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;
using P = pair<int, int>;
// Coodinate Compression
template<typename T=int>
struct CC {
bool initialized;
vector<T> xs;
CC(): initialized(false) {}
void add(T x) { xs.push_back(x);}
void init() {
sort(xs.begin(), xs.end());
xs.erase(unique(xs.begin(),xs.end()),xs.end());
initialized = true;
}
int operator()(T x) {
if (!initialized) init();
return upper_bound(xs.begin(), xs.end(), x) - xs.begin() - 1;
}
T operator[](int i) {
if (!initialized) init();
return xs[i];
}
int size() {
if (!initialized) init();
return xs.size();
}
};
const int INF = 1001001001;
int op(int a, int b) { return min(a, b); }
int e() { return INF; }
int main() {
int n;
cin >> n;
map<int, vector<P>> ps;
CC bs;
rep(i, n) {
vector<int> a(3);
rep(j, 3) cin >> a[j];
sort(a.begin(), a.end());
ps[a[0]].emplace_back(a[1], a[2]);
bs.add(a[1]);
}
segtree<int, op, e> t(bs.size());
for (auto [a, bc] : ps) {
for (auto [b, c] : bc) {
b = bs(b);
if (t.prod(0, b) < c) {
puts("Yes");
return 0;
}
}
for (auto [b, c] : bc) {
b = bs(b);
t.set(b, min(c, t.get(b)));
}
}
puts("No");
return 0;
}

T7: Ban Permutation

注意到这里的 X 比较小,所以可以考虑容斥

只需遍历 {1,2,,N} 的所有子集,将每个子集对应的以下的值加起来即可:

  • 对于 S 中的每个 i 都满足 |Pii|<X 的排列 P 的个数 × (1)|S|

下面考虑子集 S 中的每个 i 都满足 |Pii|<X 的排列 P 的个数:

dp[i][j][s] 表示在 P 的前 i 项中先确定了 j 个数,其中介于 iXi+X 之间的以 2 进制表示的整数集合为 s

代码实现
#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;
using mint = modint998244353;
using ll = long long;
using P = pair<int, int>;
int main() {
int n, x;
cin >> n >> x;
--x;
int w = x*2, w2 = 1<<w;
vector dp(1, vector<mint>(w2));
dp[0][(1<<x)-1] = 1;
rep(i, n) {
vector p(i+2, vector<mint>(w2));
swap(dp, p);
rep(j, i+1)rep(s, w2) {
mint now = p[j][s];
if (now == 0) continue;
dp[j][s>>1] += now;
rep(k, w+1) if (~s>>k&1) {
dp[j+1][(s|1<<k)>>1] += now;
}
}
}
vector<mint> fact(n+1, 1);
rep(i, n) fact[i+1] = fact[i]*(i+1);
mint ans;
rep(j, n+1)rep(s, w2) {
mint now = dp[j][s];
if (now == 0) continue;
if (s >= (1<<x)) continue;
ans += now*fact[n-j]*mint(-1).pow(j);
}
cout << ans.val() << '\n';
return 0;
}