T1:Nine

\(A+1 = B\)\(A \% 3 = 1\) 时,说明 \(A\)\(B\) 相邻

代码实现
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\) 天所吃的药的总和为 \(s_i\),我们可以将相同的 \(s_i\) 划分为一组

比如样例1:

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

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

考虑对序列 \((a_i, s_i)\)\(a_i\) 的大小进行升序排序
可以发现,如果 \(s_{i+1} \neq s_i\),那么 \(s_{i+1}\) 一定在新的组中,我们只需考虑同一个 \(s_i\) 第一次出现的位置即可
于是,我们可以仅对序列 \((a_i, b_i)\)\(a_i\) 的大小进行升序排序
在我们扫描序列 \((a_i, b_i)\) 时,可以通过当前组的 \(s\) 减去 \(b_i\) 就能得到新的一组的 \(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\) 和点 \(N_1+N_2\) 开始的最长路

代码实现
#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\) 除了保险对象以外,还需将保险补偿交给他的子孙 \(y-1\) 代人,所以要将 \(y-1\) 挂在点 \(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

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

具体地,我们可以先将 \((h_i, w_i, d_i)\)\(h_i\) 的大小做升序排序,再枚举 \((h_i, w_i, d_i)\),调查之前有没有 \((h_j, w_j, d_j)\) 满足 \(w_j < w_i\)\(d_j < d_i\),此时我们可以不管 \(h_i\),那么这个问题就是个二维偏序问题:我们需要对 \(w_i\) 进行离散化并作为 \(x\) 坐标,将 \(d_i\) 作为 \(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, \cdots, N\}\) 的所有子集,将每个子集对应的以下的值加起来即可:

  • 对于 \(S\) 中的每个 \(i\) 都满足 \(|P_i-i| < X\) 的排列 \(P\) 的个数 \(\times ~ (-1)^{|S|}\)

下面考虑子集 \(S\) 中的每个 \(i\) 都满足 \(|P_i-i| < X\) 的排列 \(P\) 的个数:

dp[i][j][s] 表示在 \(P\) 的前 \(i\) 项中先确定了 \(j\) 个数,其中介于 \(i-X \sim i+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;
}