ICPC 2026 Asia Online Test 1 解题报告

N. Red Sequence

考场上成功推到最后一步,但是因为忘记 cdq 遗憾离场。

首先定义 \(dp_{i}\) 表示到第 \(i\) 个为止的最长长度。 \(dp_i=\max{(dp_{i-1}, dp_j+(i-j))}=\max{(dp_{i-1},(dp_j-j)+i)}\)。但是这样的 \(j\) 是有要求的!我们考虑一下前缀和。假如说红黄蓝的前缀和分别是 \((Sa_i,Sb_i,Sc_i)\) 那么上面式子的 \(j\) 有这样的要求。

\(\begin{equation}\left\{\begin{aligned}Sa_i-Sa_j\ge Sb_i-Sb_j\\Sa_i-Sa_j\ge Sc_i-Sc_j\end{aligned}\right.\end{equation}\)

移项化简我们可以得到

\(\begin{equation}\left\{\begin{aligned}Sa_i-Sb_i\ge Sa_j-Sb_j\\Sa_i-Sc_i\ge Sa_j-Sc_j\end{aligned}\right.\end{equation}\)

因为对于每个 \(i\)\(i\) 是确定的,于是我们可以把前面的东西看成常数,我们不妨把 \((x_i,y_i)=(Sa_i-Sb_i,Sa_i-Sc_i)\) 这个问题扔到平面上。

于是这就是要求

\(\begin{equation}\left\{\begin{aligned}x_j\leq x_i\\y_j\leq y_i\end{aligned}\right.\end{equation}\)

满足这个要求的点的 \(dp-j\) 的最大值。直接跑 cdq 分治就行了。

我们甚至还有 \(x_i,y_i\in[-10^6,10^6]\) 于是不需要离散化。稍微平移一下坐标系就可以了。

本质是一个三维偏序问题,时间复杂度 \(\mathcal{O}(n\log^2n)\),评测机牛逼,能跑过 \(n=10^6\)

#include<bits/stdc++.h>
#define rep(I, J, K) for(int I = (int)J; I <= (int)K; ++I)
#define per(I, J, K) for(int I = (int)J; I >= (int)K; --I)
using namespace std;
const int N = 3e6 + 10;

int n, Sa[N], Sb[N], Sc[N];

struct Point {
    int x, y, z, id;
    bool operator < (const Point &point) const {
        return (y == point.y) ? z < point.z : y < point.y;
    }
}a[N];

int dp[N];

const int minf = 0xcfcfcfcf;

struct BIT {
    int val[N];
    const int MAXN = 2e6 + 10;
    int lowbit(int x) { return x & -x; }
    void insert(int x, int va) {
        while(x <= MAXN) {
            val[x] = max(val[x], va);
            x += lowbit(x);
        }
    }
    void init() {
        memset(val, 0xcf, sizeof val);
    }
    void Clear(int x) {
        while(x <= MAXN) {
            val[x] = minf;
            x += lowbit(x);
        }
    }
    int Query(int x) {
        int res = minf;
        while(x) {
            res = max(res, val[x]);
            x -= lowbit(x);
       }
       return res;
    }
}bit;

// 明确一下 dp 的定义 dp 表示到第 i 个为止的答案
// dp_j + (i-j) 满足这样要求的 j 其实是 [j + 1,i] 中间刚好是 i-j+1 个数,好像并没有什么问题?

void solve(int l, int r) {
    if(l == r) return;
    int mid = l + r >> 1;
    solve(l, mid);
    vector<Point> left(a + l, a + mid + 1);
    vector<Point> right(a + mid + 1, a + r + 1);
    sort(left.begin(), left.end());
    sort(right.begin(), right.end());
    int i = 0;
    for (auto &p : right) {
        while (i < left.size() && left[i].y <= p.y) {
            bit.insert(left[i].z, dp[left[i].id] - left[i].id);
            ++i;
        }
        dp[p.id] = max(dp[p.id], bit.Query(p.z) + p.id);
    }
    rep(k, 0, i - 1) bit.Clear(left[k].z);
    rep(i, mid + 1, r) dp[i] = max(dp[i - 1], dp[i]);
    solve(mid + 1, r);
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    rep(i, 1, n) cin >> Sa[i] >> Sb[i] >> Sc[i];
    rep(i, 1, n) Sa[i] += Sa[i - 1], Sb[i] += Sb[i - 1], Sc[i] += Sc[i - 1];
    rep(i, 0, n) a[i].x = a[i].id = i, a[i].y = Sa[i] - Sb[i] + 1000000 + 1, a[i].z = Sa[i] - Sc[i] + 1000000 + 1;
    bit.init();
    memset(dp, 0xcf, sizeof dp);
    dp[0] = 0;
    solve(0, n);
    rep(i, 1, n) cerr << dp[i] << ' ';
    cerr << endl;
    cout << dp[n] << endl;
    return 0;
}

G. Toll Gates on a Tight Schedule

考场上队友写出来了,赛后补一下。

首先能想到我们可以把 \(1\)\(n\) 的所有路径按照到 \(1\) 的距离分层,那么假如说第 \(i\) 层有 \(c_i\) 个节点,那么第 \(i\) 层给答案的最多贡献就是 \(\times c_i\)。那么根据经典结论,全部拆成 \(e\) 乘积是最大的,整数的话就是 \(2\)\(3\) 了。那么也大概就是一个 \(\mathcal{O}(3^{\frac{n}{3}})\) 的一个东西。很容易想到 meet in the middle。我们能拆成一个 \(\mathcal{O}(3^\frac{n}{6})\) 的两个东西合并,\(x+y\leq C\)。我们把 \(x\)\(y\) 序列排序之后双指针就可以了。

最后总时间复杂度是 \(\mathcal{O}(n3^\frac{n}{6})\)\(n=90\) 的情况下过的还是比较极限的。

当然有一些小细节需要注意:比如说 \(1\)\(n\) 本身路径可能不是很多,所以他可能诱导你让你的划分在 \(1\) 这种边缘位置,但是实际上不在 \(1\)\(n\) 路径上的点很多,这些点占用了很多内存空间,所以要注意把这些点去掉。

调了一天,确实难写。

#include<bits/stdc++.h>
#define rep(I, J, K) for(int I = (int)J; I <= (int)K; ++I)
#define per(I, J, K) for(int I = (int)J; I >= (int)K; --I)
using namespace std;
const int N = 5e5 + 10;

typedef long long ll;
typedef unsigned long long ull;
int n, m;
ll C, c[N];
vector<int>G[N], _G[N];
bool OKK[N];

int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m >> C;
    rep(i, 1, n) cin >> c[i];
    rep(i, 1, m) {
        int x, y;
        cin >> x >> y;
        G[x].push_back(y);
        _G[y].push_back(x);
    }
    // 先 bfs 求出最短路
    queue<int>Q;
    Q.push(1);
    vector<int>dis(n + 5, 0), _dis(n + 5, 0);
    while(!Q.empty()) {
        int u = Q.front(); Q.pop();
        for(int v : G[u]) {
            if(v != 1 && !dis[v]) {
                dis[v] = dis[u] + 1;
                Q.push(v);
            }
        }
    }
    Q.push(n);
    while(!Q.empty()) {
        int u = Q.front(); Q.pop();
        for(int v : _G[u]) {
            if(v != n && !_dis[v]) {
                _dis[v] = _dis[u] + 1;
                Q.push(v);
            }
        }
    }
    rep(u, 1, n) OKK[u] = (dis[u] + _dis[u] != dis[n]);
    if(n == 1) {
        cout << (c[1] <= C) << '\n';
        return 0;
    }
    if(!dis[n]) {
        puts("0");
        return 0;
    }
    vector<ll>ways(n + 5), _ways(n + 5);
    ways[1] = 1;
    rep(i, 0, dis[n] - 1) {
        // 现在是第 i 层
        rep(j, 1, n) if(dis[j] == i) {
            for(int v : G[j]) if(dis[v] == i + 1) ways[v] += ways[j];
        }
    }
    _ways[n] = 1;
    per(i, dis[n], 1) {
        rep(j, 1, n) if(dis[j] == i) {
            for(int v : _G[j]) if(dis[v] == i - 1) _ways[v] += _ways[j];
        }
    }
    int maxd = -1;
    ll maxx = 1ll << 62;
    // rep(i, 1, n) cerr << i << ' ' << dis[i] << ' ' << ways[i] << ' ' << _ways[i] << '\n';
    rep(i, 0, dis[n]) {
        ll mx = 0;
        rep(j, 1, n) if(dis[j] == i) {
            mx = max(mx, max(ways[j], _ways[j]));
            // cerr << ways[j] << ' ' << _ways[j] << ' ' << i << '\n';
            if(mx >= maxx) break;
        }
        cerr << i << ' ' << maxx << '\n';
        if(mx < maxx) {
            maxx = mx;
            maxd = i;
        }
    }
    // 前面走 maxd 步.
    cerr << maxd << ' ' << dis[n] << '\n';
    vector<vector<ll>>Cost(n + 5, vector<ll>()), _Cost(n + 5, vector<ll>());
    vector<bool>vis(n + 5, 0);
    // 前面走 maxd 步.
    Cost[1].push_back(c[1]);
    Q.push(1);
    while(!Q.empty()) {
        int u = Q.front(); Q.pop();
        for(int v : G[u]) {
            if(dis[v] == dis[u] + 1 && dis[v] <= maxd && !OKK[v]) {
                if(Cost[v].empty()) Cost[v].reserve(min(ways[v], 2000000ll));
                for(ll &x : Cost[u]) if(x + c[v] <= C) Cost[v].push_back(x + c[v]);
                if(!vis[v]) Q.push(v), vis[v] = 1;
            }
        }
    }
    rep(i, 1, n) vis[i] = 0;
    Q.push(n);
    _Cost[n].push_back(c[n]);
    while(!Q.empty()) {
        int u = Q.front(); Q.pop();
        for(int v : _G[u]) {
            if(dis[v] == dis[u] - 1 && dis[v] >= maxd && !OKK[v]) {
                if(_Cost[v].empty()) _Cost[v].reserve(min(_ways[v], 2000000ll));
                for(ll &x : _Cost[u]) if(x + c[v] <= C) _Cost[v].push_back(x + c[v]);
                if(!vis[v]) vis[v] = 1, Q.push(v);
            }
        }
    }
    ull res = 0;
    // cout << maxd << endl;
    rep(i, 1, n) {
        if(dis[i] == maxd) {
            if(Cost[i].empty() || _Cost[i].empty()) continue;
            sort(Cost[i].begin(), Cost[i].end());
            sort(_Cost[i].begin(), _Cost[i].end());
            // cerr << i << ":\n";
            // for(ll x : Cost[i]) cerr << x << ' ';
            // cerr << '\n';
            // for(ll x : _Cost[i]) cerr << x << ' ';
            // cerr << '\n';
            int k = _Cost[i].size() - 1;
            rep(j, 0, Cost[i].size() - 1) {
                while(k >= 0 && _Cost[i][k] + Cost[i][j] > C + c[i]) --k;
                res += (k + 1);
            }
        }
    }
    cout << res << '\n';
    return 0;
}
posted @ 2026-09-07 17:48  Mercury_City  阅读(86)  评论(0)    收藏  举报