• [NOIP2017] 列队


    题目链接

    题意

      有一个 $n$ 行 $m$ 列的方阵,初始时第 $i$ 行第 $j$ 列的学生的编号是 $(i-1) imes 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
    ", 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
  • 相关阅读:
    PHP开发者常犯的MySQL错误
    linux 用户管理
    php中mysql数据库异步查询实现
    PHP 安全相关 简单知识
    js倒计时 网上流传最多的
    TP学习笔记
    Java Map
    Java集合技巧
    Java集合之HashSet/TreeSet原理
    Java Set
  • 原文地址:https://www.cnblogs.com/Pedesis/p/10992924.html
Copyright © 2020-2023  润新知