1 条题解

  • 0
    @ 2026-10-27 1:47:48

    思路与算法

    把每行前m-1列各看作一个动态序列,最后一列单独维护。每次先从最后列取出第x项v;若y<m,再从行x取第y项作为答案,将v追加该行;否则答案就是v。最后把答案追加最后列。用动态开点线段树维护仍存在的位置数,支持删除第k项与尾部追加。初始编号按公式算,只有后来追加的值显式存储。

    正确性说明

    向左标齐只改离队所在行,之后向上标齐只改最后列,因此上述两个序列操作恰对应原矩阵变化。线段树每个结点记录区间剩余项数,左子树计数决定第k项在哪一侧,删除后沿路径减计数、追加时增计数,保持序列秩与位置对应。原始前缀未开结点的计数按区间与初始范围交集长度计算,省略存储不改变逻辑。

    复杂度

    令U=max(n,m)+q。含行根数组与所有操作,时间O(n+q log U),空间O(n+q log U);不分配n乘m矩阵。

    易错点

    m=1时行内序列为空,只操作最后列。编号最大n*m达9×10^10,需64位。动态结点池扩容时不能保留可能失效的vector元素引用。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    
    struct Node {
        int l, r, c;
    };
    struct Seq {
        vector<Node> t;
        Seq(int q) {
            t.reserve((size_t)q * 45 + 1);
            t.push_back({0, 0, 0});
        }
        int initial(int l, int r, int len) {
            return max(0, min(r, len) - l + 1);
        }
        int count(int p, int l, int r, int len) {
            return p ? t[p].c : initial(l, r, len);
        }
        int change(int p, int l, int r, int pos, int delta, int len) {
            if (!p) {
                p = t.size();
                t.push_back({0, 0, initial(l, r, len)});
            }
            t[p].c += delta;
            if (l < r) {
                int mid = (l + r) / 2;
                if (pos <= mid) {
                    int x = change(t[p].l, l, mid, pos, delta, len);
                    t[p].l = x;
                } else {
                    int x = change(t[p].r, mid + 1, r, pos, delta, len);
                    t[p].r = x;
                }
            }
            return p;
        }
        int kth(int p, int l, int r, int k, int len) {
            if (!p)
                return l + k - 1;
            if (l == r)
                return l;
            int mid = (l + r) / 2, left = count(t[p].l, l, mid, len);
            return k <= left ? kth(t[p].l, l, mid, k, len) : kth(t[p].r, mid + 1, r, k - left, len);
        }
    };
    int main() {
        int n, m, q;
        cin >> n >> m >> q;
        Seq st(q);
        vector<int> roots(n + 1), append(n + 1);
        vector<vector<long long>> extra(n + 1);
        vector<long long> last;
        int global = 0, limit = max(n, m) + q + 1;
        auto pop = [&](int &root, int k, int len) {
            int pos = st.kth(root, 1, limit, k, len);
            root = st.change(root, 1, limit, pos, -1, len);
            return pos;
        };
        while (q--) {
            int x, y;
            cin >> x >> y;
            int gp = pop(global, x, n);
            long long from = gp <= n ? 1LL * gp * m : last[gp - n - 1], answer;
            if (y == m)
                answer = from;
            else {
                int p = pop(roots[x], y, m - 1);
                answer = p < m ? 1LL * (x - 1) * m + p : extra[x][p - m];
                extra[x].push_back(from);
                int where = m - 1 + extra[x].size();
                roots[x] = st.change(roots[x], 1, limit, where, 1, m - 1);
            }
            last.push_back(answer);
            global = st.change(global, 1, limit, n + last.size(), 1, n);
            cout << answer << "\n";
        }
    }
    
    • 1

    信息

    ID
    1970
    时间
    2000ms
    内存
    512MiB
    难度
    普及+
    标签
    递交数
    0
    已通过
    0
    上传者