2026 ICPC Asia EC 网络预选赛第一场 N 题思路分享(二维偏序,二维树状数组,dp)

题目链接

题意

给定长度为 \(n\) 的序列,每个位置有红、黄、蓝棋子各至多一枚.

若一段连续区间内,红棋子数量均不少于黄棋子数量和蓝棋子数量,则称该区间合法,求所有合法区间长度之和的最大值.

\(1\le n \le 10^6\).

思路

考虑 \(dp\),令 \(dp_i\) 为处理到 \(i\) 时的最大合法区间长度和,转移如下:

\[dp_i = \max(dp_{i-1},\max_{j\lt i}(dp_j+i-j)) \]

其中,必须满足区间 \([j+1,i]\) 合法,可以用前缀和刻画,即满足:

\[\begin{cases} preR_i-preY_i \ge preR_j-preY_j \\ preR_i-preB_i \ge preR_j-preB_j \end{cases} \]

这是一个二维偏序约束,使用二维树状数组维护 \(\max_{j\lt i}(dp_j-j)\) 即可.

二维树状数组实现有几点需要注意:

  • 第二维不需要离散化.

  • 提前获取参与更新和查询的点对 \((x,y)\),需要把 \(y\) 传给所有 \(x\) 往后跳 \(lowbit\) 的位置.

  • 查询时二分的坐标不一定存在.

时间复杂度 \(\mathcal{O}(n\log^2 n)\).

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int INF = 1e9;

struct fenwick{
    int n;
    vector<vector<int>> pos,mx;

    fenwick(int _n){
        n = _n;
        pos.assign(n+1,{-INF});
        mx.resize(n+1);
    }

    void build(vector<pair<int,int>>& vec){
        for (auto& [x,y]:vec){
            for (int i=x;i<=n;i+=i&-i){
                pos[i].push_back(y);
            }
        }   

        for (int i=1;i<=n;i++){
            sort(pos[i].begin()+1,pos[i].end());
            pos[i].erase(unique(pos[i].begin()+1,pos[i].end()),pos[i].end());
            int m = pos[i].size()-1;
            mx[i].assign(m+1,-INF);
        }
    }

    void update(int x,int y,int val){
        for (int i=x;i<=n;i+=i&-i){
            int m = pos[i].size()-1;
            int rk = lower_bound(pos[i].begin()+1,pos[i].end(),y)-pos[i].begin();
            
            for (int j=rk;j<=m;j+=j&-j){
                mx[i][j] = max(mx[i][j],val);
            }
        }
    }

    int query(int x,int y){
        int res = -INF;
        for (int i=x;i>=1;i-=i&-i){
            int m = pos[i].size()-1;
            int rk = upper_bound(pos[i].begin()+1,pos[i].end(),y)-pos[i].begin()-1;

            for (int j=rk;j>=1;j-=j&-j){
                res = max(res,mx[i][j]);
            }
        }
        return res;
    }
};

void solve(){
    int n;
    cin >> n;

    vector<array<int,3>> pre(n+1,{0,0,0});
    for (int i=1;i<=n;i++){
        for (int j=0;j<3;j++){
            int x;
            cin >> x;
            pre[i][j] = pre[i-1][j]+x;
        }
    }

    vector<pair<int,int>> wait(n+1);
    wait[0] = {0,0};
    vector<int> uni{-INF};
    uni.push_back(0);

    for (int i=1;i<=n;i++){
        wait[i] = {pre[i][0]-pre[i][1],pre[i][0]-pre[i][2]};
        uni.push_back(pre[i][0]-pre[i][1]);
    }

    sort(uni.begin()+1,uni.end());
    uni.erase(unique(uni.begin()+1,uni.end()),uni.end());
    int m = uni.size()-1;

    for (int i=0;i<=n;i++){
        wait[i].first = lower_bound(uni.begin()+1,uni.end(),wait[i].first)-uni.begin();
    }

    fenwick fw(m);
    fw.build(wait);
    fw.update(wait[0].first,wait[0].second,0);
    vector<int> dp(n+1);

    for (int i=1;i<=n;i++){
        dp[i] = max(dp[i-1],i+fw.query(wait[i].first,wait[i].second));
        fw.update(wait[i].first,wait[i].second,dp[i]-i);
    }

    cout << dp[n] << '\n';
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int t = 1;
    // cin >> t;
    while (t--) solve();

    return 0;
}
posted @ 2026-09-09 22:30  kzssCCC  阅读(67)  评论(0)    收藏  举报