1 条题解

  • 0
    @ 2025-10-27 23:41:33

    题目大意

    nn 个学生,每人有一个不同的运动能力值 aia_i(1 到 nn)。学生按顺序排成一列,老师从左到右依次评分,分数为 1 到 nn 的不同整数。

    评分需要满足两个条件:

    1. 能力约束:能力强的学生分数不能低于能力弱的学生
    2. 顺序约束:右边的学生分数不能低于左边的学生

    在满足条件的前提下,老师希望使用尽可能多的不同分数。

    每节课后会有两个学生交换位置,需要求出每节课评分时最多能使用的不同分数种类。

    解题思路

    问题分析

    这个问题可以转化为:给定一个排列,找到满足以下条件的最长序列的长度:

    • 序列在原始排列中是递增的(顺序约束)
    • 序列对应的能力值也是递增的(能力约束)

    换句话说,我们需要找到排列和能力值序列的最长公共递增子序列

    关键观察

    pos[x]pos[x] 表示能力值为 xx 的学生在队列中的位置。

    条件可以重新解释为:

    • 如果我们选择一个分数序列 s1,s2,,sks_1, s_2, \ldots, s_k,那么:
      1. pos[s1]<pos[s2]<<pos[sk]pos[s_1] < pos[s_2] < \cdots < pos[s_k](顺序约束)
      2. s1<s2<<sks_1 < s_2 < \cdots < s_k(能力约束)

    因此,问题转化为:在能力值序列上,找到最长的递增子序列,使得这些能力值对应的位置也是递增的。

    动态规划解法

    dp[i]dp[i] 表示以能力值 ii 结尾的最长合法序列长度。

    转移方程:

    $$dp[i] = 1 + \max\{dp[j] \mid j < i \text{ 且 } pos[j] < pos[i]\} $$

    这个 DP 可以在 O(n2)O(n^2) 时间内计算,但对于 n106n \leq 10^6 来说太慢。

    数据结构优化

    我们需要维护一个数据结构,支持:

    • 查询所有 j<ij < ipos[j]<pos[i]pos[j] < pos[i]dp[j]dp[j] 的最大值
    • 插入 dp[i]dp[i] 到位置 pos[i]pos[i]

    这可以用树状数组(Fenwick Tree)线段树来实现。

    具体步骤:

    1. 按能力值从小到大处理每个学生
    2. 对于能力值 ii,查询位置在 pos[i]pos[i] 之前的所有 dpdp 值的最大值
    3. dp[i]=查询结果+1dp[i] = \text{查询结果} + 1
    4. 在位置 pos[i]pos[i] 更新值为 dp[i]dp[i]

    时间复杂度:O(nlogn)O(n \log n)

    处理交换操作

    当两个学生交换位置时,我们需要动态维护这个 DP 值。

    交换位置 ppqq 的学生:

    • 设这两个学生的能力值分别为 xxyy
    • 交换 pos[x]pos[x]pos[y]pos[y]
    • 重新计算所有受影响的 dpdp

    但是直接重新计算所有受影响的 dpdp 值在最坏情况下是 O(n)O(n) 的,对于 z300000z \leq 300000 来说不可行。

    优化策略

    观察 1:答案的单调性

    交换操作通常只会影响局部的 dpdp 值,整体答案变化不大。

    观察 2:只更新必要的值

    当交换 pos[x]pos[x]pos[y]pos[y] 时,只有能力值在 xxyy 之间的学生可能受到影响。

    具体来说,需要重新计算:

    • 能力值在 [min(x,y),max(x,y)][\min(x,y), \max(x,y)] 范围内的学生
    • 这些学生的 dpdp 值可能依赖于交换的两个位置

    高效实现

    1. 使用平衡树或线段树维护每个位置的 dpdp
    2. 交换时,只重新计算受影响区间内的 dpdp
    3. 使用懒惰传播或增量更新来优化

    代码框架

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 1000005;
    
    int n, z;
    int a[MAXN], pos[MAXN], dp[MAXN];
    
    // 树状数组用于查询前缀最大值
    struct Fenwick {
        vector<int> tree;
        int size;
        
        Fenwick(int n) : size(n), tree(n + 1, 0) {}
        
        void update(int idx, int val) {
            while (idx <= size) {
                tree[idx] = max(tree[idx], val);
                idx += idx & -idx;
            }
        }
        
        int query(int idx) {
            int res = 0;
            while (idx > 0) {
                res = max(res, tree[idx]);
                idx -= idx & -idx;
            }
            return res;
        }
    };
    
    int solve() {
        Fenwick bit(n);
        int ans = 0;
        
        // 按能力值从小到大处理
        for (int i = 1; i <= n; i++) {
            dp[i] = bit.query(pos[i]) + 1;
            bit.update(pos[i], dp[i]);
            ans = max(ans, dp[i]);
        }
        
        return ans;
    }
    
    void swap_students(int p, int q) {
        // 交换位置p和q的学生
        int x = a[p], y = a[q];
        swap(a[p], a[q]);
        swap(pos[x], pos[y]);
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);
        
        cin >> n >> z;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            pos[a[i]] = i;
        }
        
        // 输出第一节课的答案
        cout << solve() << "\n";
        
        for (int i = 1; i < z; i++) {
            int p, q;
            cin >> p >> q;
            swap_students(p, q);
            
            // 这里需要优化:只重新计算受影响的部分
            // 简化版本:直接调用solve()(对于小数据)
            cout << solve() << "\n";
        }
        
        return 0;
    }
    

    进一步优化

    对于大数据(n,z300000n, z \leq 300000),需要更精细的实现:

    1. 局部更新:只重新计算受交换影响的学生
    2. 批量处理:将多个交换操作一起处理
    3. 分摊复杂度:使用更高效的数据结构

    算法标签

    • 动态规划
    • 树状数组/线段树
    • 最长递增子序列
    • 数据结构优化
    • 交换操作处理

    复杂度分析

    • 初始解法:O(nlogn)O(n \log n)
    • 每次交换:最优可达到 O(logn)O(\log n)O(log2n)O(\log^2 n)
    • 总复杂度:O((n+z)logn)O((n + z) \log n)

    这个问题的难点在于高效处理动态的交换操作,需要巧妙的数据结构设计和局部更新策略。

    • 1

    信息

    ID
    4218
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    24
    已通过
    1
    上传者