C. Palindromic in Both Bases

枚举前一半的数位

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

using namespace std;
using ll = long long;

bool isPalindrome(ll x, int a) {
    string s;
    while (x) {
        s +='0'+x%a;
        x /= a;
    }
    string rs = s;
    ranges::reverse(rs);
    return s == rs;
}

int main() {
    int a; ll n;
    cin >> a >> n;
    
    ll ans = 0;
    auto check = [&](string s) {
        ll y = stoll(s);
        if (y <= n and isPalindrome(y, a)) ans += y;
    };
    for (ll x = 1; x < 1e6; ++x) {
        string s = to_string(x);
        string rs = s;
        ranges::reverse(rs);
        s += rs;
        
        check(s);
        s.erase(s.begin()+(s.size()/2));
        check(s);
    }
    
    cout << ans << '\n';
    
    return 0;
}

D. Transmission Mission

先将坐标排序
与其直接考虑“放置 \(M\) 个基站”,不如将其转化为“设置 \(M\) 个区间”,这样更容易理解

具体操作:

  • 把问题看作在坐标 \(X_1\)\(X_2\) 之间、\(X_2\)\(X_3\) 之间,\(\cdots\) 等位置划分区间
  • 需要选择 \(M-1\) 个间隔不包含任何基站,这样剩下的部分自然构成 \(M\) 个区间
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<ll> x(n);
    rep(i, n) cin >> x[i];
    ranges::sort(x);
    
    vector<ll> d;
    rep(i, n-1) d.push_back(x[i+1]-x[i]);
    ranges::sort(d);
    
    rep(i, m-1) d.pop_back();
    ll ans = 0;
    for (ll nd : d) ans += nd;
    
    cout << ans << '\n';
    
    return 0;
}

E. Count A%B=C

不难发现满足条件的三元组一定满足 \(a > b > c\)
这样就简单了
答案就是 \(\frac{N(N+1)}{2} - \sum\limits_{b} \lfloor\dfrac{N}{b}\rfloor\)
右边的这个式子就是整除分块的板子题

代码实现
#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;
using mint = modint998244353;

int main() {
    ll n;
    cin >> n;
    
    mint ans = mint(n+1)*n/2;
    for (ll b = 1; b <= n;) {
        ll y = n/b;
        ll nb = n/y+1;
        ans -= mint(nb-b)*y;
        b = nb;
    }
    
    cout << ans.val() << '\n';
    
    return 0;
}

F. Jump Traveling

dp[v][pre][k] 表示当前位于点 \(k\),上一个访问的点是 \(pre\),已经进行了 \(k\) 次操作
状态数为 \(O(NK)\),转移数为 \(O(N)\),总复杂度为 \(O(N^2K)\),显然超时

注意到,从状态 dp[v][pre][k] 进行转移时,唯一不可访问的点是 pre,因此:

  • 只需从 dp[v][pre][k] 进行一次有效转移
  • 无需重复其他 dp[v][*][k] 对同一 \(v\)\(k\) 的转移

实现方法:

  • 记录点 \(v\) 已完成的扩散次数
  • 通过智能跳过冗余计算,将总复杂度降至 \(O(NK)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

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

void solve() {
    int n, k;
    cin >> n >> k;
    
    vector<P> es;
    vector<vector<P>> g(n);
    rep(i, n-1) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        es.emplace_back(a, b);
        g[a].emplace_back(b, i);
        g[b].emplace_back(a, n-1+i);
    }
    
    const int INF = 1001001001;
    vector dist((n-1)*2, vector<int>(k, INF));
    vector cnt(n, vector<int>(k));
    queue<P> q;
    auto push = [&](int ei, int d) {
        int w = d%k;
        if (dist[ei][w] != INF) return;
        dist[ei][w] = d;
        q.emplace(ei, d);
    };
    for (auto [to, ei] : g[0]) push(ei, 1);
    while (q.size()) {
        auto [ei, d] = q.front(); q.pop();
        int v;
        {
            auto [a, b] = es[ei%(n-1)];
            if (ei < n-1) v = b; else v = a;
        }
        if ((++cnt[v][d%k]) <= 2) {
            for (auto [to, ej] : g[v]) {
                if (d%k != 0 and ei%(n-1) == ej%(n-1)) continue;
                push(ej, d+1);
            }
        }
    }
    
    vector<int> ans(n, INF);
    rep(i, (n-1)*2) {
        auto [a, b] = es[i%(n-1)];
        if (i >= n-1) swap(a, b);
        ans[b] = min(ans[b], dist[i][0]);
    }
    for (int i = 1; i < n; ++i) {
        if (ans[i] == INF) ans[i] = -1; else ans[i] /= k;
        cout << ans[i] << " \n"[i == n-1];
    }
}

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

G. AtCoder Express 4

线段树优化建图:构建四棵线段树(两棵正向树、两棵反向树),用于高效表示区间关系。

树结构设计:

  • 每棵线段树的节点代表一个车站区间。
  • 正向树(子节点指向父节点)用于下车操作。
  • 反向树(父节点指向子节点)用于上车操作。
  • 针对东西方向(左→右和右→左)分别设计两种树结构。

列车连接处理:

  • 对于每趟列车,创建虚拟节点。
  • 使用线段树将上车区间连接到虚拟节点,再将虚拟节点连接到下车区间。
  • 票价计算:基础票价 + 上车车站到上车区间端点的距离 + 下车区间端点到下车车站的距离。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

struct Node {
    int li, ri, lc, rc;
};

int main() {
    cin.tie(nullptr) -> sync_with_stdio(false);
    
    int n, m;
    cin >> n >> m;
    
    vector<ll> x(n);
    rep(i, n) cin >> x[i];
    
    vector<Node> nodes;
    rep(i, n) nodes.emplace_back(i, i+1, -1, -1);
    vector<vector<pair<int, ll>>> g(n);
    
    vector<int> roots;
    // 0: right in 
    // 1: right out
    // 2: left in 
    // 3: left out
    rep(sign, 2) {
        rep(dir, 2) {
            auto dfs = [&](auto& f, int l, int r) -> int {
                if (r-l == 1) return l;
                int c = (l+r)/2;
                int lc = f(f, l, c);
                int rc = f(f, c, r);
                int v = nodes.size();
                nodes.emplace_back(l, r, lc, rc);
                g.push_back({});
                ll lcost = 0, rcost = 0;
                if (sign == 0) rcost = x[c]-x[l];
                else lcost = x[r-1]-x[c-1];
                if (dir == 0) {
                    g[v].emplace_back(lc, lcost);
                    g[v].emplace_back(rc, rcost);
                }
                else {
                    g[lc].emplace_back(v, lcost);
                    g[rc].emplace_back(v, rcost);
                }
                return v;
            };
            roots.push_back(dfs(dfs, 0, n));
        }
    }
    
    rep(mi, m) {
        int sl, sr, tl, tr; ll c;
        cin >> sl >> sr >> tl >> tr >> c;
        --sl; --tl;
        int nv = g.size(); g.push_back({});
        
        auto dfs = [&](auto& f, int type, int l, int r, int v) -> void {
            auto [li, ri, lc, rc] = nodes[v];
            if (ri <= l or r <= li) return;
            if (l <= li and ri <= r) {
                ll cost = 0;
                if (type&2) cost = x[r-1]-x[ri-1];
                else cost = x[li]-x[l];
                if (type&1) g[v].emplace_back(nv, cost+c);
                else g[nv].emplace_back(v, cost);
                return;
            }
            f(f, type, l, r, lc);
            f(f, type, l, r, rc);
        };
        
        if (sr <= tl) {
            c += x[tl]-x[sr-1];
            dfs(dfs, 3, sl, sr, roots[3]);
            dfs(dfs, 0, tl, tr, roots[0]);
        }
        else {
            c += x[sl]-x[tr-1];
            dfs(dfs, 1, sl, sr, roots[1]);
            dfs(dfs, 2, tl, tr, roots[2]);
        }
    }
    
    const ll INF = 1e18;
    int vs = g.size();
    vector<ll> dist(vs, INF);
    using P = pair<ll, int>;
    priority_queue<P, vector<P>, greater<P>> q;
    
    dist[0] = 0; q.emplace(0, 0);
    while (q.size()) {
        auto [d, v] = q.top(); q.pop();
        if (dist[v] != d) continue;
        for (auto [u, c] : g[v]) {
            ll nd = d+c;
            if (dist[u] <= nd) continue;
            dist[u] = nd;
            q.emplace(nd, u);
        }
    }
    
    for (int i = 1; i < n; ++i) {
        ll ans = dist[i];
        if (ans == INF) ans = -1;
        cout << ans << ' ';
    }
    
    return 0;
}