洛谷 P3960 列队

Sylvia 是一个热爱学习的女孩子。

前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。

Sylvia 所在的方阵中有\(n\times m\)名学生,方阵的行数为n,列数为m。

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中的学生从1到\(n\times m\)编上了号码(参见后面的样例)。即:初始时,第i行第j列的学生的编号是\((i-1)\times m+j\)

然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天中,一共发生了q件这样的离队事件。每一次离队事件可以用数对\((x,y) (1 \le x \le n, 1 \le y \le m)\)描述,表示第x行第y列的学生离队。

在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达这样的两条指令:

向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条指令之后,空位在第x行第m列。

向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条指令之后,空位在第n行第m列。

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后,下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第n行第m列一个空位,这时这个学生会自然地填补到这个位置。

因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学的编号是多少。

注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。

考虑用线段树计算每一行第x个人的位置,因为n和m都很大,所以要动态开点,线段树的最长长度就是\(max(n,m)+p\)

计算第x人的位置很好算,维护一个区间内有多少个人被删除掉,查询的时候用类似于主席树的方法查询就可以了

这样删除操作也就说完了

而添加时我们开n+1个vector,1~n维护前m-1个人,第n+1维护最后一列,这是因为我们每次操作是先左移再上移

对于一对(x,y),如果\(y=m\),则只需要上移,所以直接在第n+1个vector里操作然后把这个数丢到vector最后就好了;否则就要先左移,需要查出第x个vector的第y个,删掉后丢到vector最后,然后上移,在第n+1个vector进行类似的操作

然后算编号也是很好算的,左移操作中如果我们得到的位置\(ans <= m-1\),那么编号为\((x-1)*n+ans\),否则为vector中第\([x][ans-m]\)个数;上移操作中如果\(ans <= n\),那么编号为\(ans*m\),否则为vector中第\([n+1][ans-n-1]\)个数(vector下标是从0开始的)

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#define LL long long
const int N = 3e5;
using namespace std;
int n,m,q,rt[N + 5],lc[N * 20 + 5],rc[N * 20 + 5],len,node_cnt;
vector <LL> d[N + 5];
struct Seg
{
    int del[N * 20 + 5];
    int query(int k,int l,int r,int x)
    {
        if (l == r)
            return l;
        int mid = l + r >> 1,y = mid - l + 1 - del[lc[k]];
        if (y >= x)
            return query(lc[k],l,mid,x);
        else
            return query(rc[k],mid + 1,r,x - y);
    }
    void change(int &k,int l,int r,int x)
    {
        if (!k)
            k = ++node_cnt;
        if (l == r)
        {
            del[k]++;
            return;
        }
        int mid = l + r >> 1;
        if (x <= mid)
            change(lc[k],l,mid,x);
        else
            change(rc[k],mid + 1,r,x);
        del[k] = del[lc[k]] + del[rc[k]];
    }
    LL ask1(int x,LL y)
    {
        LL ans = query(rt[n + 1],1,len,x);      
        change(rt[n + 1],1,len,ans);
        if (ans <= n)
            ans *= 1ll * m;
        else
            ans = d[n + 1][ans - n - 1];
        d[n + 1].push_back(y ? y : ans);
        return ans;
    }
    LL ask2(int x,int y)
    {
        LL ans = query(rt[x],1,len,y);
        change(rt[x],1,len,ans);
        if (ans <= m - 1)
            ans = 1ll * (x - 1) * m + ans;
        else
            ans = d[x][ans - m];
        d[x].push_back(ask1(x,ans));
        return ans;
    }
}tree;
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    len = max(n,m) + q;
    int x,y;
    for (int i = 1;i <= q;i++)
    {
        scanf("%d%d",&x,&y);
        if (y == m)
            printf("%lld\n",tree.ask1(x,0));
        else
            printf("%lld\n",tree.ask2(x,y));
    }
    return 0;
}
posted @ 2020-06-08 20:31  eee_hoho  阅读(167)  评论(0编辑  收藏  举报