zoukankan      html  css  js  c++  java
  • [NOIp 2017]列队

    Description

    Sylvia 是一个热爱学习的女♂孩子。
    前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。
    Sylvia 所在的方阵中有n×m名学生,方阵的行数为 n,列数为 m。
    为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 1 到 n×m 编上了号码(参见后面的样例)。即:初始时,第 iii 行第 jjj 列 的学生的编号是(i−1)×m+j。
    然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 q 件这样的离队事件。每一次离队事件可以用数对(x,y)(1≤x≤n,1≤y≤m)描述,表示第 x 行第 y 列的学生离队。
    在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:

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

    向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 n 行第 m 列。
    教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 n 行 第 m 列一个空位,这时这个学生会自然地填补到这个位置。
    因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学 的编号是多少。
    注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。

    正解:线段树/树状数组/平衡树

    转载自http://www.cnblogs.com/Yuzao/p/7920813.html#3867765

    (30\%):开个 (n*m) 的数组模拟即可
    (50\%):发现只有500行有改动,所以单独拿出这500行和最后一列,模拟即可.
    (80\%):只有一行的话,我们就开一个 (m+q) 的数组,然后树状数组维护每一个位置是否有人,并且维护每一个位置的人的id,这样就会产生很多空位,询问就是查找第 (y) 个有人的位置的id,二分+树状数组 或 直接线段树查找第k大即可,与 (70) 分不同的是,还需要再维护最后一列,像行一样维护即可
    (100\%):和 (80) 分类似,想到有很多位置根本没有大的变动,我们像之前一样,我们把只需要出队的位置删除即可,所以我们维护每一个位置是否被删,但是不太好存,所以用动态开点线段树标记删除位置,然后像之前一样二分找出第 (y) 个有人的位置即可,还有一个不同的是,id数组需要动态维护,所以开个vector存即可,所以100和80的区别仅在于是否使用动态内存.

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<vector>
     6 using namespace std;
     7 typedef long long lol;
     8 const int N=300000;
     9 int chl[10000005],chr[10000000+5],cnt[10000000+5],root[N+5],tot;
    10 vector<lol>G[N+5];
    11 lol n,m,sum,q;
    12 lol query(int rt,int l,int r,lol k)
    13 {
    14     if (l==r) return l;
    15     int mid=(l+r)/2;
    16     if (mid-l+1-cnt[chl[rt]]>=k) return query(chl[rt],l,mid,k);
    17     else return query(chr[rt],mid+1,r,k-(mid-l+1-cnt[chl[rt]]));
    18 }
    19 void del(int &rt,int l,int r,lol k)
    20 {
    21     if (!rt) rt=++tot;
    22     cnt[rt]++;
    23     if (l==r) return;
    24     int mid=(l+r)/2;
    25     if (k<=mid) del(chl[rt],l,mid,k);
    26     else del(chr[rt],mid+1,r,k);
    27 }
    28 lol zyys1(lol x,lol kind)
    29 {
    30     lol pos=query(root[n+1],1,sum,x);
    31     del(root[n+1],1,sum,pos);
    32     lol ans=0;
    33     if (pos<=n) ans=m*pos;
    34     else ans=G[n+1][pos-n-1];
    35     if (kind==0) G[n+1].push_back(ans);
    36     else G[n+1].push_back(kind);
    37     return ans; 
    38 }
    39 lol zyys2(lol x,lol y)
    40 {
    41     lol pos=query(root[x],1,sum,y);
    42     del(root[x],1,sum,pos);
    43     lol ans=0;
    44     if (pos<m) ans=(x-1)*m+pos;
    45     else ans=G[x][pos-m];
    46     G[x].push_back(zyys1(x,ans));
    47     return ans;
    48 }
    49 int main()
    50 {lol x,y;
    51 lol ans;
    52     cin>>n>>m>>q;
    53     sum=max(n,m)+q;
    54     while (q--)
    55     {
    56         scanf("%lld%lld",&x,&y);
    57         if (y==m) ans=zyys1(x,0);
    58         else ans=zyys2(x,y);
    59         printf("%lld
    ",ans);
    60     }
    61 }
  • 相关阅读:
    洛谷 P1074 靶形数独 Label:search 不会
    TYVJ P3522 &&洛谷 P1135 奇怪的电梯 Label:bfs
    洛谷 P1160 队列安排 Label:链表 数据结构
    uestc 1073 秋实大哥与线段树 Label:线段树
    TYVJ P3407 佳佳的魔法照片 Label:语文很重要 语文很重要 语文很重要
    TYVJ P1103 多项式输出 Label:模拟 有点儿坑
    A+B Problem 详细解答 (转载)
    如何批量修改文件名
    c++ 在windows下获取时间和计算时间差的几种方法总结
    SQL Server 2008在Windows 10上不支持
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/8073183.html
Copyright © 2011-2022 走看看