1 solutions

  • 0
    @ 2025-5-14 10:32:06

    本例中需要支持如下操作,在 (n×mn \times m ) 的方阵中,如果让 ((x,y))((x,y) ) 出方阵,那么

    • 找到标号第 (xx ) 大的行,并且在该行中找到第 (yy ) 大的列,将对应的元素 ((x,y))((x,y) ) 取出
    • 然后把该行中 ([y+1cdotsm])([y+1 cdots m] ) 整体往前挪一位
    • 然后操作第 (m)(m ) 列,将第 (m)(m )([x+1cdotsn])([x+1 cdots n] ) 整体往上挪一位
    • 最后把取出来的元素插入 ((n,m))((n,m) )

    注意到我们需要对 (n)(n ) 行,以及第 (m)(m ) 列求 前缀第 (k ) 大

    可以考虑一开始建 (n+1)(n+1 ) 棵线段树,其中每一棵线段树初始只有一个根节点

    这样第 ([1n])([1 \dots n] ) 棵线段树维护 ([1n])([1 \rightarrow n] ) 行,第 (n+1)(n+1 ) 棵线段树维护第 (m)(m )

    将线段树写成 动态开点,这样就可以尝试维护操作了,维护区间内被删除了多少个数 (cnt)(cnt )

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=300010,M=N*21;
    int n,m,Q;
    struct Node{
        int l,r,cnt;
    }tr[M];
    int root[N],idx;
    vector<LL> g[N];
    int query(int &u,int l,int r,int x)
    {
        if(!u) u=++idx;
        if(l==r) return r;
        int mid=l+r>>1;
        int left=mid-l+1-tr[tr[u].l].cnt;
        if(x<=left) return query(tr[u].l,l,mid,x); //当前x在左边
        return query(tr[u].r,mid+1,r,x-left); //当前x在右边
    }
    void update(int &u,int l,int r,int x)
    {
        if(l==r) tr[u].cnt++;//多了一个
        else{
            int mid=l+r>>1;
            if(x<=mid) update(tr[u].l,l,mid,x);
            else update(tr[u].r,mid+1,r,x);
            tr[u].cnt=tr[tr[u].l].cnt+tr[tr[u].r].cnt; //根据子节点计算当前节点
        }
    }
    int main()
    {
        cin>>n>>m>>Q;
        int L=max(n,m)+Q;
        while(Q--)
        {
            int x,y;
            cin>>x>>y;
            if(y==m) //最后一列
            {
                int r=query(root[n+1],1,L,x); //最后一列的线段树中找到第x个数
                update(root[n+1],1,L,r);
                LL id=r<=n?(r-1ll)*m+m:g[n+1][r-n-1]; //计算位置
                cout<<id<<endl;
                g[n+1].push_back(id); //删除的这个数放到最后一个位置上
            }
            else
            {
                int c=query(root[x],1,L,y); //在x行循环第y个元素
                update(root[x],1,L,c); //删除对应的元素
                LL id=c<m?(x-1ll)*m+c:g[x][c-m]; //得到对应的下标
                cout<<id<<endl; 
                g[n+1].push_back(id); //删除的元素放到最后一列
                int r=query(root[n+1],1,L,x); //最后一列从上往下的第x个元素
                update(root[n+1],1,L,r);
                id=r<=n?(r-1ll)*m+m:g[n+1][r-n-1]; //得到编号
                g[x].push_back(id); //放到第x个线段树之中
            }
        }
        return 0;
    }
    
    • 1

    Information

    ID
    1129
    Time
    2000ms
    Memory
    256MiB
    Difficulty
    10
    Tags
    # Submissions
    1
    Accepted
    1
    Uploaded By