C. Flapping Takahashi

维护当前时刻 \(t\) 可达的高度区间 \([\text{lh}, \text{rh}]\) 。从时刻 \(t_{i-1}\)\(t_i\) 区间扩散为 \([\text{lh}-\Delta t, \text{rh} + \Delta t]\) 。 然后对区间 \([l, u]\) 取交集。

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

void solve() {
    int n, h;
    cin >> n >> h;
    
    int now = 0, lh = h, rh = h;
    bool ans = true;
    rep(i, n) {
        int t, l, u;
        cin >> t >> l >> u;
        lh -= t-now; rh += t-now;
        now = t;
        lh = max(lh, l);
        rh = min(rh, u);
        if (lh > rh) ans = false;
    }
    
    if (ans) puts("Yes");
    else puts("No");
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

D. Clouds

二维差分
显然只有被 \(1\) 朵云覆盖的格子才会影响答案

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

const int M = 2000;
int sky[M+1][M+1];
int one[M+1][M+1];

struct Cloud { int u, d, l, r; };

int main() {
    int n;
    cin >> n;
    
    vector<Cloud> clouds;
    rep(i, n) {
        int u, d, l, r;
        cin >> u >> d >> l >> r;
        --u; --l;
        sky[u][l]++;
        sky[u][r]--;
        sky[d][l]--;
        sky[d][r]++;
        clouds.emplace_back(u, d, l, r);
    }
    rep(i, M)rep(j, M) sky[i+1][j] += sky[i][j];
    rep(i, M)rep(j, M) sky[i][j+1] += sky[i][j];
    
    rep(i, M)rep(j, M) one[i+1][j+1] = sky[i][j]==1;
    rep(i, M)rep(j, M+1) one[i+1][j] += one[i][j];
    rep(i, M+1)rep(j, M) one[i][j+1] += one[i][j];
    
    int base = 0;
    rep(i, M)rep(j, M) if (sky[i][j] == 0) base++;
    
    rep(i, n) {
        auto [u, d, l, r] = clouds[i];
        int dif = one[d][r] - one[d][l] - one[u][r] + one[u][l];
        int ans = base + dif;
        cout << ans << '\n';
    }
    
    return 0;
}

E. Distribute Bunnies

一种做法是二分图最大匹配
另一种做法是单独考虑每个无向图连通分量,为其定向:

  • 边数 \(<\) 点数,树
  • 边数 \(=\) 点数,基环树
  • 边数 \(>\) 点数,把重边删掉就是等于的情况
代码实现
#include <bits/stdc++.h>
#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();
  }
};

int main() {
    int m;
    cin >> m;
    
    vector<P> bunnies;
    CC cc;
    rep(i, m) {
        int x, r;
        cin >> x >> r;
        bunnies.emplace_back(x-r, x+r);
        cc.add(x-r); cc.add(x+r);
    }
    
    int n = cc.size();
    vector<vector<int>> to(n);
    rep(i, m) {
        auto [a, b] = bunnies[i];
        a = cc(a); b = cc(b);
        to[a].push_back(b);
        to[b].push_back(a);
    }
    
    int ans = 0;
    vector<bool> used(n);
    rep(i, n) if (!used[i]) {
        int edge = 0, vertex = 0;
        auto dfs = [&](auto& f, int v) -> void {
            if (used[v]) return;
            vertex++; edge += to[v].size();
            used[v] = true;
            for (int u : to[v]) f(f, u);
        };
        dfs(dfs, i);
        edge /= 2;
        ans += min(vertex, edge);
    }
    
    cout << ans << '\n';
    
    return 0;
}

F. Concat (2nd)

\(S_i + S_j < S_j + S_i\) 排序
显然直接排显然超时
有两种优化方法:

  • \(\text{shuffle}\) 一遍再排
  • \(z\) 算法优化比较函数
代码实现1
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
 
using namespace std;

void solve() {
    int n;
    cin >> n;
    
    vector<string> s(n);
    rep(i, n) cin >> s[i];
    
    random_shuffle(s.begin(), s.end());
    sort(s.begin(), s.end(), [&](const string& a, const string& b) {
        return a+b < b+a;
    });
    
    auto cat = [&]() {
        string res;
        for (string& t : s) res += t;
        return res;
    };
    
    rep(i, n-1) {
        if (s[i]+s[i+1] == s[i+1]+s[i]) {
            cout << cat() << '\n';
            return;
        }
    }
    
    swap(s[n-2], s[n-1]);
    string ans = cat();
    swap(s[n-2], s[n-1]);
    if (n >= 3) {
        swap(s[n-2], s[n-3]);
        ans = min(ans, cat());
    }
    cout << ans << '\n';
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}
代码实现2
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
 
using namespace std;

void solve() {
    int n;
    cin >> n;
    
    vector<string> s(n);
    rep(i, n) cin >> s[i];
    
    vector<vector<int>> z(n);
    rep(i, n) z[i] = z_algorithm(s[i]);
    
    auto comp = [&](int i, int j) {
        int w = s[i].size();
        if (s[i] != s[j].substr(0, w)) return (s[i] < s[j])?-1:1;
        int r = s[j].size()-w;
        if (r > 0 and z[j][w] < r) {
            int k = z[j][w];
            return (s[j][k] < s[j][w+k])?-1:1;
        }
        return (s[j].substr(r) < s[i])?-1:1;
    };
    
    vector<int> p(n);
    rep(i, n) p[i] = i;
    sort(p.begin(), p.end(), [&](int i, int j) {
        if (s[i].size() > s[j].size()) return comp(j, i) == 1;
        return comp(i, j) == -1;
    });
    {
        auto old = s;
        rep(i, n) s[i] = old[p[i]];
    }
    
    auto cat = [&]() {
        string res;
        for (string& t : s) res += t;
        return res;
    };
    
    rep(i, n-1) {
        if (s[i]+s[i+1] == s[i+1]+s[i]) {
            cout << cat() << '\n';
            return;
        }
    }
    
    swap(s[n-2], s[n-1]);
    string ans = cat();
    swap(s[n-2], s[n-1]);
    if (n >= 3) {
        swap(s[n-2], s[n-3]);
        ans = min(ans, cat());
    }
    cout << ans << '\n';
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}