[NOIP2017] 列队

题目链接

题意

  有一个 $n$ 行 $m$ 列的方阵,初始时第 $i$ 行第 $j$ 列的学生的编号是 $(i-1) \times m+j$。每当有一个同学离队后(不能有两人同时离队),方阵中所有学生会先向左看齐,再向前看齐,然后离队学生归队后补在第 $n$ 行第 $m$ 列的位置上。给定每次离队学生的位置,求该学生的编号。

分析

  观察可知每次学生离队只会对该行,最后一行和最后一列产生影响

  我们可以建 $n+1$ 棵支持单点修改的线段树,前 $n$ 棵包含第 $n$ 行的前 $m-1$ 个学生,第 $n+1$ 棵包含最后一列

  所以对于位置为 $(x,y)$ 的学生离开,只需要修改第 $x$ 棵和第 $n+1$ 棵线段树,也就是先输出并删去 $x$ 树的第 $y$ 个元素,将其加入 $n+1$ 树的最后一个位置,再删去 $n+1$ 树的第 $n$ 个元素,将其加入 $x$ 树的最后一个位置

  因此每棵线段树的最大长度为 $max(n,m)+q$($q$ 为询问次数)

  由于本题数据较大,开 $n+1$ 棵线段树肯定会超内存,所以需要动态开点

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
#define ll long long
#define inf 0x7fffffff
#define N 300005
#define M 10000005

int n, m, q, a, num, now;
int id[N], len[N], sz[M], ls[M], rs[M];
ll val[M];

inline int get_sz(int l,int r) {
    if (now == n + 1) {
        if (r <= n) return r - l + 1;
        if (l <= n) return n - l + 1;
        return 0;
    }
    if (r < m) return r - l + 1;
    if (l < m) return m - l;
    return 0;
}

ll query(int &p, int l, int r, int x) {
    if (!p) {
        p = ++num;
        sz[p] = get_sz(l, r);
        if (l == r) {
            if (now <= n) val[p] = (ll)(now - 1) * m + l;
            else val[p] = (ll)l * m;
        }
    }
    sz[p]--;
    if (l == r) return val[p];
    int mid = (l + r) >> 1;
    if ((!ls[p] && x <= (mid - l + 1)) || x <= sz[ls[p]]) 
        return query(ls[p], l, mid, x);
    else {
        if (!ls[p]) x -= (mid - l + 1);
        else x -= sz[ls[p]];
        return query(rs[p], mid + 1, r, x);
    }
}

void update(int &p, int l, int r, int x, ll w) {
    if(!p) {
        p = ++num;
        sz[p] = get_sz(l,r);
        if (l == r) val[p] = w;
    }
    sz[p]++;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (x <= mid) update(ls[p], l, mid, x, w);
    else update(rs[p], mid + 1, r, x, w);
}

int main() {
    scanf("%d%d%d", &n, &m, &q);
    a = max(n, m) + q;
    for (int i = 1; i <= q; i++) {
        int x, y; ll z;
        scanf("%d%d", &x, &y);
        if (y == m) now = n + 1, z = query(id[now], 1, a, x);
        else now = x, z = query(id[now], 1, a, y);
        printf("%lld\n", z);
        now = n + 1; update(id[now], 1, a, n + (++len[now]), z);
        if (y != m) {
            z = query(id[now], 1, a, x);
            now = x; update(id[now], 1, a, m - 1 + (++len[now]), z);
        }
    }

    return 0;
}
View Code

转载于:https://www.cnblogs.com/Pedesis/p/10992924.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值