zoukankan      html  css  js  c++  java
  • 【NOIP2017】列队【可持久化线段树】

    题目链接

      

    题目描述

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

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

    Sylvia 所在的方阵中有n×mn×m名学生,方阵的行数为 nn,列数为 mm。

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

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

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

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

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

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

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

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

    输入输出格式

    输入格式:

    输入共 q+1q+1 行。

    第 1 行包含 3 个用空格分隔的正整数 n,m,qn,m,q,表示方阵大小是 nn 行 mm 列,一共发 生了 qq 次事件。

    接下来 qq 行按照事件发生顺序描述了 qq 件事件。每一行是两个整数 x,yx,y,用一个空 格分隔,表示这个离队事件中离队的学生当时排在第 xx 行第 yy 列。

    输出格式:

    按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学 生的编号。

      刚拿到这道题的时候,的确是无从下手的感觉,知道有这样的一道题,【SCOI2006】动态最值,这样一道题,但是那道题是维护再一维空间上的删除并且挪动这样的一个操作,我们对于那道题只需要开线段树去维护(甚至不需要可持久化维护),维护的是size,也就是这段区间上有几个数,然后我们直接维护对应的最小值和最大值即可,线段树区间的长度就是原来的N个数的长度(因为只有N个数的说,并且还是只有删除操作)。然后一开始的时候,我们直接buildtree,并且直接赋值即可,然后在后面的找区间的时候,左区间超过L个数、右区间共有R个数,这样的(用size来维护)。

    这里讲一下该怎么去维护:

    我们假设左子树有X个节点,向右区间查找的时候,记得减去X,

    如果ql<=X && qr>X,那么我们左右都要查;

    如果ql>X && qr>X,那么我们直接查右区间;

    如果ql<=X && qr<=X,我们只查左区间。

    同样的,我们也可以删除。

      讲了这么多与这道题没有关系的,接下来回归到正题上来。

      思路:上面讲了这些,是为了开拓在可持久化线段树上的思路的,并且用到这道题就可以了。关键在于是怎么用?这道题会有这样的两种操作,一种是我们拎出来的人在第M列,那么他就不需要(操作1)向左移动了,直接向前移动即可;另一种是他不在第M列,此时我们就要考虑他要先向左移动,然后在向前对齐(向前对齐的时候,是不是就是可以看成我们挪动的是第M列的数)。

      有了这些思路之后,就可以开始写了,因为这里的区间长度会比较的大,很显然,我们不能去用线段树来直接维护,那么就是需要用到可持久化线段树了,先想个办法维护一下在第M列时候的情况,因为这时候我们只需要向前看齐就可以了,那么我们是不是可以看成单链的一维空间那样子的想法(就是上面的思想了),但是这里需要补,怎么补就是个问题了,我们在这里可以开一个vector<>来存,把每个最后的值就是直接pushback就可以了。

      那么,假如我们拎出去的人不是第M列的怎么办呢?假如拎出去的人的坐标是(x, y),那么我们是不是要去x这一行去找到y位的这个数,然后得到他的实际值,这时候(x, y)这个点空出来了,先要这一列的其他人都靠过来,然后再是第M列的人往前走,靠过来的做法与之前一维的时候相同,但是第M列的人向前走呢,我们可以看到M列的人,从第x位开始空出来,后面的人向前走,是不是就可以相当是第x位以后的点都向前移了一位,而查第M列的第x位不就是查其后面的数呢,然后这时候,就要把原来(x, y)上的值给赋值到M列的最后一个,也就是pushback()进去即可,同时还要把在M列x行后面的x+1行的值给塞到第x行的pushback后面去。

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cmath>
      4 #include <string>
      5 #include <cstring>
      6 #include <algorithm>
      7 #include <limits>
      8 #include <vector>
      9 #include <stack>
     10 #include <queue>
     11 #include <set>
     12 #include <map>
     13 #define lowbit(x) ( x&(-x) )
     14 #define pi 3.141592653589793
     15 #define e 2.718281828459045
     16 #define INF 0x3f3f3f3f
     17 #define HalF (l + r)>>1
     18 #define lsn rt<<1
     19 #define rsn rt<<1|1
     20 #define Lson lsn, l, mid
     21 #define Rson rsn, mid+1, r
     22 #define QL Lson, ql, qr
     23 #define QR Rson, ql, qr
     24 #define myself rt, l, r
     25 namespace fastIO {
     26 #define BUF_SIZE 100000
     27     //fread -> read
     28     bool IOerror = 0;
     29     inline char nc() {
     30         static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
     31         if(p1 == pend) {
     32             p1 = buf;
     33             pend = buf + fread(buf, 1, BUF_SIZE, stdin);
     34             if(pend == p1) {
     35                 IOerror = 1;
     36                 return -1;
     37             }
     38         }
     39         return *p1++;
     40     }
     41     inline bool blank(char ch) {
     42         return ch == ' ' || ch == '
    ' || ch == '
    ' || ch == '	';
     43     }
     44     inline void read(int &x) {
     45         char ch;
     46         while(blank(ch = nc()));
     47         if(IOerror) return;
     48         for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
     49     }
     50 #undef BUF_SIZE
     51 };
     52 using namespace fastIO;
     53 using namespace std;
     54 typedef unsigned long long ull;
     55 typedef long long ll;
     56 const int maxN = 6e5 + 7;
     57 int N, M, Q, root[maxN], _UP, lc[30 * maxN], rc[30 * maxN], siz[30 * maxN], tot;
     58 vector<ll> vt[maxN];
     59 inline void insert(int &rt, int l, int r, int k)
     60 {
     61     if(!rt) rt = ++tot;
     62     siz[rt]++;
     63     if(l == r) return;
     64     int mid = HalF;
     65     if(k <= mid) insert(lc[rt], l, mid, k);
     66     else insert(rc[rt], mid + 1, r, k);
     67 }
     68 int query(int rt, int l, int r, int k)
     69 {
     70     if(l == r) return l;
     71     int mid = HalF, size_L = mid - l + 1 - siz[lc[rt]];
     72     if(k <= size_L) return query(lc[rt], l, mid, k);
     73     else return query(rc[rt], mid + 1, r, k - size_L);
     74 }
     75 inline ll del_M_las(int x, ll val)    //如果这个人是最后那一排的话,就不需要向左看齐了
     76 {
     77     int pos = query(root[N + 1], 1, _UP, x);    insert(root[N + 1], 1, _UP, pos);
     78     ll ans = pos <= N ? (ll)pos * (ll)M : vt[N + 1][pos - N - 1];
     79     vt[N + 1].push_back(val ? val : ans);
     80     return ans;
     81 }
     82 inline ll del_N(int x, int y)
     83 {
     84     int pos = query(root[x], 1, _UP, y);    insert(root[x], 1, _UP, pos);
     85     ll ans = pos < M ? (ll)(x - 1) * (ll)M + pos : vt[x][pos - M];
     86     vt[x].push_back(del_M_las(x, ans));
     87     return ans;
     88 }
     89 int main()
     90 {
     91     tot = 0;
     92     scanf("%d%d%d", &N, &M, &Q);
     93     //read(N);    read(M);    read(Q);
     94     _UP = max(N, M) + Q;
     95     int x, y;
     96     while(Q--)
     97     {
     98         scanf("%d%d", &x, &y);
     99         //read(x);    read(y);
    100         if(y == M) printf("%lld
    ", del_M_las(x, 0));
    101         else printf("%lld
    ", del_N(x, y));
    102     }
    103     return 0;
    104 }
    View Code
  • 相关阅读:
    webshell
    隐写术的总结
    A&DCTF
    JavaScript数组去重方法汇总
    Reverse Words in a String III
    DOM编程 --《高性能JavaScript》
    数据存储 --《高性能JavaScript》
    加载和执行 --《高性能JavaScript》
    算法和流程控制 --《高性能JavaScript》
    重载类型转换操作符(overload conversion operator)
  • 原文地址:https://www.cnblogs.com/WuliWuliiii/p/10841619.html
Copyright © 2011-2022 走看看