C. Rotatable Array

首先 \(k\) 中只有 \(k\%N\) 次操作有用
执行 \(i\) 次操作后,点 \(p\) 位置上的数就会变成 \((p+i)\%N\) 位置上的数
对于操作 \(3\),用一个变量 offset 记录 \(k\%N\) 的累加和即可

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

using namespace std;

int main() {
    int n, q;
    cin >> n >> q;
    
    vector<int> a(n);
    rep(i, n) a[i] = i+1;
    
    int offset = 0;
    rep(qi, q) {
        int type;
        cin >> type;
        if (type == 3) {
            int k;
            cin >> k;
            offset = (offset+k)%n;
        }
        else {
            int p;
            cin >> p;
            --p;
            int i = (offset+p)%n;
            if (type == 1) {
                int x;
                cin >> x;
                a[i] = x;
            }
            else {
                cout << a[i] << '\n';
            }
        }
    }
    
    return 0;
}

D. XOR Shortest Walk

爆搜

dp[v][x] 表示走到点 \(v\) 且当前的异或和为 \(x\) 的路径是否存在

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

using namespace std;

struct Edge {
    int to, w;
};

int main() {
    int n, m;
    cin >> n >> m;
    int W = 1<<10;
    
    vector<vector<Edge>> g(n);
    rep(i, m) {
        int a, b, w;
        cin >> a >> b >> w;
        --a; --b;
        g[a].emplace_back(b, w);
    }
    
    vector<bool> used(n*W);
    queue<int> q;
    auto push = [&](int v, int w) {
        int vid = v*W+w;
        if (used[vid]) return;
        used[vid] = true;
        q.push(vid);
    };
    push(0, 0);
    while (q.size()) {
        int vid = q.front(); q.pop();
        int v = vid/W, x = vid%W;
        for (auto [u, w] : g[v]) push(u, x^w);
    }
    
    rep(x, W) {
        if (used[(n-1)*W+x]) {
            cout << x << '\n';
            return 0;
        }
    }
    
    puts("-1");
    
    return 0;
}

ps:换成无向图也是可以做的

E. Battles in a Row

简单dp

dp[i][j][k] 表示到第 \(i\) 只怪物为止,生命值剩下 \(j\) 点,魔法值剩下 \(k\)

状态数就是 \(O(NHM)\),显然超时

这个是个 bool 型的dp,取值为 0/1

考虑将第 \(3\) 维拿掉,记 dp[i][j] 表示到第 \(i\) 只怪物为止,生命值剩下 至少 \(j\) 点时剩余魔法值的最大值

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

using namespace std;

inline void chmax(int& x, int y) { if (x < y) x = y; }

int main() {
    int n, h, m;
    cin >> n >> h >> m;
    
    vector<int> dp(h+1, -1);
    dp[h] = m;
    rep(i, n) {
        int a, b;
        cin >> a >> b;
        
        vector<int> old(h+1, -1);
        swap(dp, old);
        
        rep(j, h+1) if (old[j] != -1) {
            if (a <= j) {
                chmax(dp[j-a], old[j]);
            }
            if (b <= old[j]) {
                chmax(dp[j], old[j]-b);
            }
        }
        if (dp == vector<int>(h+1, -1)) {
            cout << i << '\n';
            return 0;
        }
    }
    
    cout << n << '\n';
    
    return 0;
}

ps:也可以加一个逃跑的行动

F. Balanced Rectangles

实际上就是求有多少个子矩阵的累加和为 \(0\)
一维问题很好做,二维可以将它拍扁就变成一维了(枚举两行,对于每一列只考虑它的累加和)
可以做到 \(O(H^2W)\),看上去似乎会超时,但实际上并不会
假设 \(H \leqslant W\)\(H^2 \leqslant HW \leqslant 3 \times 10^5=M\)\(\Rightarrow \ H \leqslant \sqrt{M}\)
那么 \(O(H^2W)=O(M\sqrt{M})\)

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

using namespace std;
using ll = long long;

void solve() {
    int h, w;
    cin >> h >> w;
    
    vector<string> s(h);
    rep(i, h) cin >> s[i];
    if (h > w) {
        swap(h, w);
        vector<string> old(h, string(w, '.'));
        swap(s, old);
        rep(i, h)rep(j, w) s[i][j] = old[j][i];
    }
    
    int n = h*w;
    vector<int> cnt(n*2+1);
    
    ll ans = 0;
    rep(si, h) {
        vector<int> a(w);
        for (int ti = si; ti < h; ++ti) {
            rep(j, w) a[j] +=  s[ti][j] == '#' ? 1 : -1;
            vector<int> sum(w+1);
            sum[0] = n;
            rep(j, w) sum[j+1] = sum[j]+a[j];
            rep(j, w) {
                cnt[sum[j]]++;
                ans += cnt[sum[j+1]];
            }
            rep(j, w) cnt[sum[j]] = 0;
        }
    }
    
    cout << ans << '\n';
}

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

G. Longest Chord Chain

从某点切开后,这题就转化成了求解关于前缀和后缀的最大重叠层数问题。最长嵌套弦链,将每条弦按端点较大的那个点的编号做升序排序的话,就消除掉了一维,然后用线段树加速一下即可

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

using namespace std;
using P = pair<int, int>;

int op(int a, int b) { return max(a, b); }
int e() { return 0; }

int main() {
    int n;
    cin >> n;
    
    vector<P> ps;
    rep(i, n) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        if (a > b) swap(a, b);
        ps.emplace_back(b, a);
    }
    ranges::sort(ps);
    
    int ans = 0;
    // dp[r]:以 r 为最外层弦右端点的嵌套弦链的最大长度
    // lis[l]:以 l 为最外层弦左端点的嵌套弦链的最大长度
    segtree<int, op, e> lis(n*2), dp(n*2);
    for (auto [r, l] : ps) {
        int now = lis.prod(l, r)+1;
        ans = max(ans, now+dp.prod(0, l));
        dp.set(r, now);
        lis.set(l, now);
    }
    
    cout << ans << '\n';
    
    return 0;
}