C. Range Search Query

遍历每个位置 \(i\),判断 \(S\) 中从 \(i\) 开始的长度为 \(m\) 的子串是否和 \(T\) 匹配,如果是则记 \(o[i] = 1\),否则记 \(o[i] = 0\)
然后维护一个 \(\text{next}\) 数组,\(\text{next}[i]\) 表示从 \(i\) 开始向右出现的第一个 \(T\) 的起始下标

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

using namespace std;

int main() {
    int q;
    string s, t;
    cin >> q >> s >> t;
    int n = s.size(), m = t.size();
    
    vector<int> o(n);
    rep(i, n) {
        if (s.substr(i, m) == t) o[i] = 1;
    }
    
    vector<int> nxt(n+1, 1e9);
    for (int i = n-1; i >= 0; --i) {
        nxt[i] = nxt[i+1];
        if (o[i]) nxt[i] = i;
    }
    
    rep(qi, q) {
        int l, r;
        cin >> l >> r;
        --l;
        int ni = nxt[l];
        if (ni+m <= r) puts("Yes");
        else puts("No");
    }
    
    return 0;
}

D. Masking Tape

使用时间戳来记录“最近一次发生的全体变色”与每个格子的“最近一次变成未放置(即移除瓦片)”的时刻,用这些信息判断在某个区间内是否发生了影响该格子的第二种操作。

代码实现
#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;
    
    string ans(n, 'a');
    int last = -1; char c = 'a';
    vector<int> t(n, -1);
    vector<bool> tile(n);
    
    rep(qi, q) {
        int type;
        cin >> type;
        if (type == 1) {
            int i;
            cin >> i;
            --i;
            if (tile[i]) {
                t[i] = qi;
            }
            else {
                if (t[i] < last) ans[i] = c;
            }
            tile[i] = !tile[i];
        }
        else {
            last = qi;
            cin >> c;
        }
    }
    
    rep(i, n) if (!tile[i]) {
        if (t[i] < last) ans[i] = c;
    }
    
    cout << ans << '\n';
    
    return 0;
}

E. Wheel Distance

先用环上正反两次松弛把每个环点到汇点的最短距离 \(d[i]\) 预处理好,再对每个查询取顺时针、逆时针弧长与经汇点 \(d[s]+d[t]\) 三者的最小值作为答案。

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

using namespace std;
using ll = long long;

inline void chmin(ll& a, ll b) { if (a > b) a = b; }

int main() {
    int n, q;
    cin >> n >> q;
    
    vector<int> a(n);
    vector<ll> d(n);
    rep(i, n) cin >> a[i];
    rep(i, n) cin >> d[i];
    
    vector<ll> sa(n+1);
    rep(i, n) sa[i+1] = sa[i]+a[i];
    
    rep(ri, 2) {
        rep(i, n) chmin(d[(i+1)%n], d[i]+a[i]);
        for (int i = n-1; i >= 0; --i) {
            chmin(d[i], d[(i+1)%n]+a[i]);
        }
    }
    
    rep(qi, q) {
        int s, t;
        cin >> s >> t;
        --s; --t;
        if (s > t) swap(s, t);
        ll ans = 0;
        if (t == n) {
            ans = d[s];
        }
        else {
            ans = d[s]+d[t];
            ll sum = sa[t]-sa[s];
            chmin(ans, sum);
            chmin(ans, sa[n]-sum);
        }
        cout << ans << '\n';
    }
    
    return 0;
}

F. Count Cells in a Window

扫描线板子

把每个查询对行区间 \([A,B]\) 的求和转成前缀差:答案 =(前 \(B\) 行在列区间
\([C,D]\) 的黑格数) −(前 \(A−1\) 行在列区间 \([C,D]\) 的黑格数)。
对于每个列区间用线段树维护区间加以及区间求和即可。

代码实现
#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 ll = long long;

struct S {
    ll sum, len;
};
S op(S a, S b) { return S(a.sum+b.sum, a.len+b.len); }
S e() { return S(0, 0); }
S mapping(ll f, S x) { return S(x.sum + f*x.len, x.len); }
ll composition(ll f, ll g) { return f+g; }
ll id() { return 0; }

int main() {
    int n, m, q;
    cin >> n >> m >> q;
    
    vector<int> L(n), R(n);
    rep(i, n) cin >> L[i] >> R[i], --L[i];
    
    vector<vector<tuple<int, int, int>>> qs(n);
    rep(qi, q) {
        int u, d, l, r;
        cin >> u >> d >> l >> r;
        u -= 2; --d; --l;
        if (u >= 0) qs[u].emplace_back(l, r, q+qi);
        qs[d].emplace_back(l, r, qi);
    }
    
    lazy_segtree<S, op, e, ll, mapping, composition, id> seg(m);
    rep(i, m) seg.set(i, S(0, 1));
    
    vector<ll> ans(q);
    rep(i, n) {
        seg.apply(L[i], R[i], 1);
        for (auto& [l, r, qi] : qs[i]) {
            int sign = 1;
            if (qi >= q) qi -= q, sign = -1;
            ans[qi] += seg.prod(l, r).sum * sign;
        }
    }
    
    rep(i, q) cout << ans[i] << '\n';
    
    return 0;
}

G. Frequency Query on Tree

树上莫队板子

记 \(\text{cnt}(y)\) 表示数值 \(y\) 在当前路径上的出现次数,\(g(i)\) 表示满足 \(\text{cnt}(y) \geqslant i\) 的数值 \(y\) 的个数
那么询问的答案就是 \(g(a)-g(b+1)\)

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

using namespace std;
using A = array<int, 6>;

template<typename T=int>
struct lca {
    int n, l;
    vector<vector<int>> to;
    vector<vector<T>> co;
    vector<int> dep;
    vector<T> costs;
    vector<vector<int>> par;
    lca(int n): n(n), to(n), co(n), dep(n), costs(n) {
        l = 0;
        while (1<<l <= n) ++l;
        par = vector<vector<int>>(n, vector<int>(l, -1));
    }
    void addEdge(int a, int b, T c=1) {
        to[a].push_back(b); co[a].push_back(c);
        to[b].push_back(a); co[b].push_back(c);
    }
    void dfs(int v, int d=0, T c=0, int p=-1) {
        par[v][0] = p;
        dep[v] = d;
        costs[v] = c;
        rep(i, to[v].size()) {
            int u = to[v][i];
            if (u == p) continue;
            dfs(u, d+1, c+co[v][i], v);
        }
    }
    void init(int root=0) {
        dfs(root);
        rep(i, l-1) {
            rep(v, n) {
                par[v][i+1] = par[v][i]==-1 ? -1 : par[par[v][i]][i];
            }
        }
    }
    // LCA
    int operator()(int a, int b) {
        if (dep[a] > dep[b]) swap(a, b);
        int gap = dep[b]-dep[a];
        for (int i = l-1; i >= 0; --i) {
            int len = 1<<i;
            if (gap >= len) {
                gap -= len;
                b = par[b][i];
            }
        }
        if (a == b) return a;
        for (int i = l-1; i >= 0; --i) {
            int na = par[a][i];
            int nb = par[b][i];
            if (na != nb) a = na, b = nb;
        }
        return par[a][0];
    }
    int length(int a, int b) {
        int c = (*this)(a, b);
        return dep[a]+dep[b]-dep[c]*2;
    }
    T dist(int a, int b) {
        int c = (*this)(a, b);
        return costs[a]+costs[b]-costs[c]*2;
    }
};

int main() {
    cin.tie(nullptr) -> sync_with_stdio(false);
    
    int n, q;
    cin >> n >> q;
    
    vector<int> x(n);
    rep(i, n) cin >> x[i], --x[i];
    
    lca g(n);
    rep(i, n-1) {
        int u, v;
        cin >> u >> v;
        --u; --v;
        g.addEdge(u, v);
    }
    g.init();
    
    vector<int> et;
    vector<int> st(n), ed(n);
    auto dfs = [&](auto& f, int v, int p=-1) -> void {
        st[v] = et.size();
        et.push_back(v);
        for (int u : g.to[v]) if (u != p) f(f, u, v);
        ed[v] = et.size();
        et.push_back(v);
    };
    dfs(dfs, 0);
    
    vector<A> qs(q);
    rep(i, q) {
        int s, t, a, b;
        cin >> s >> t >> a >> b;
        --s; --t;
        
        if (st[s] > st[t]) swap(s, t);
        
        int lca = g(s, t);
        qs[i][3] = a;
        qs[i][4] = b;
        qs[i][5] = i;
        
        if (lca == s) {
            qs[i][0] = st[s];
            qs[i][1] = st[t];
            qs[i][2] = -1;
        }
        else {
            qs[i][0] = ed[s];
            qs[i][1] = st[t];
            qs[i][2] = lca;
        }
    }
    
    int D = max<int>(1, et.size()/sqrt(q));
    vector<int> block(q);
    rep(i, q) block[i] = qs[i][0]/D;
    
    sort(qs.begin(), qs.end(), [&](const A& a, const A& b) {
        if (block[a[5]] != block[b[5]]) return block[a[5]] < block[b[5]];
        return a[1] < b[1];
    });
    
    vector<bool> used(n);
    vector<int> cnt(n), G(n+2);
    auto upd = [&](int v) {
        int nx = x[v];
        if (used[v]) {
            --G[cnt[nx]];
            --cnt[nx];
            used[v] = false;
        }
        else {
            ++cnt[nx];
            ++G[cnt[nx]];
            used[v] = true;
        }
    };
    
    vector<int> ans(q);
    int l = 0, r = -1;
    for (auto& [ql, qr, lca, a, b, qi] : qs) {
        while (r < qr) upd(et[++r]);
        while (l > ql) upd(et[--l]);
        while (r > qr) upd(et[r--]);
        while (l < ql) upd(et[l++]);
        
        if (lca != -1) upd(lca);
        
        ans[qi] = G[a]-G[b+1];
        
        if (lca != -1) upd(lca);
    }
    
    rep(i, q) cout << ans[i] << '\n';
    
    return 0;
}