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

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

    solution

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

    前50%和另外 20%

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #define RG register
    using namespace std;
    typedef long long ll;
    const int N=50005,M=300005;
    int n,m,Q,b[M],num=0,tot;
    struct node{int x,y;}e[M];
    
    namespace brute{
      ll a[505][N],line[N],r[N];
      void main(){
        for(int i=1;i<=tot;i++)
          for(int j=1;j<=m;j++)
        a[i][j]=(ll)(b[i]-1)*m+j;
        for(int i=1;i<=n;i++)
          line[i]=(ll)(i-1)*m+m;
        for(int P=1;P<=Q;P++){
          int x=e[P].x,y=e[P].y,hxy;
          printf("%lld
    ",a[x][y]);
          hxy=a[x][y];
          for(int i=y;i<m;i++)r[i]=a[x][i+1];
          for(int i=y;i<m;i++)a[x][i]=r[i];
          for(int i=b[x];i<n;i++)r[i]=line[i+1];
          for(int i=b[x];i<n;i++)line[i]=r[i];
          line[n]=hxy;
          for(int i=1;i<=tot;i++)
        a[i][m]=line[b[i]];
        }
      }
    }
    
    namespace cheat{
      int id[M*2],tr[M*2];
      void add(int sta,int ad){
        for(int i=sta;i<=m+Q;i+=(i&(-i)))tr[i]+=ad;
      }
      int qry(int sta){
        int ret=0;
        for(int i=sta;i>=1;i-=(i&(-i)))ret+=tr[i];
        return ret;
      }
      int midit(int k){
        int l=1,r=m+Q,mid,tmp,ret=0;
        while(l<=r){
          mid=(l+r)>>1;
          tmp=qry(mid);
          if(tmp>=k)ret=mid,r=mid-1;
          else l=mid+1;
        }
        return ret;
      }
      void main(){
        for(int i=1;i<=m;i++)
          add(i,1),id[i]=i;
    
        int cnt=m;
        for(int P=1;P<=Q;P++){
          int y=e[P].y;
          int p=midit(y);
          printf("%d
    ",id[p]);
          cnt++;
          add(p,-1);add(cnt,1);
          id[cnt]=id[p];
        }
      }
    }
    
    void work(){
      scanf("%d%d%d",&n,&m,&Q);
      for(int i=1;i<=Q;i++){
        scanf("%d%d",&e[i].x,&e[i].y);
        b[++num]=e[i].x;
      }
      sort(b+1,b+num+1);
      tot=unique(b+1,b+num+1)-b-1;
      for(int i=1;i<=n;i++)
        e[i].x=lower_bound(b+1,b+tot+1,e[i].x)-b;
      if(Q<=500)brute::main();
       else{
       if(n==1)cheat::main();
        else brute::main();
      }
    }
    
    int main(){
      work();
      return 0;
    }
    
    

    100分

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=300005,M=6000100;
    vector<ll>S[N];
    int n,m,Q,tot,root[N],ls[M],rs[M],v[M],cnt=0;
    
    inline void Modify(int &rt,int l,int r,int sa){
    	if(!rt)rt=++cnt;
    	v[rt]++;
    	if(l==r)return ;
    	int mid=(l+r)>>1;
    	if(sa<=mid)Modify(ls[rt],l,mid,sa);
    	else Modify(rs[rt],mid+1,r,sa);
    }
    
    inline int qry(int rt,int l,int r,int k){
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	int sum=mid-l+1-v[ls[rt]];
    	if(k<=sum)return qry(ls[rt],l,mid,k);
    	return qry(rs[rt],mid+1,r,k-sum);
    }
    
    inline ll caline(int x){
    	int r=qry(root[n+1],1,tot,x);
    	Modify(root[n+1],1,tot,r);
    	return r<=n?1ll*(r-1)*m+m:S[n+1][r-n-1];
    }
    inline ll calrow(int x,int y){
    	int r=qry(root[x],1,tot,y);
    	Modify(root[x],1,tot,r);
    	return r<m?1ll*(x-1)*m+r:S[x][r-m];
    }
    
    void work()
    {
    	int x,y;
    	ll ret,tmp;
    	scanf("%d%d%d",&n,&m,&Q);
    	tot=Max(n,m)+Q;
    	while(Q--){
    		scanf("%d%d",&x,&y);
    		if(y==m){
    			ret=caline(x);
    			S[n+1].push_back(ret);
    			printf("%lld
    ",ret);
    		}
    		else{
    			ret=calrow(x,y);
    			printf("%lld
    ",ret);
    			S[n+1].push_back(ret);
    			tmp=caline(x);
    			S[x].push_back(tmp);
    		}
    	}
    }
    
    int main()
    {
    	freopen("pp.in","r",stdin);
    	freopen("pp.out","w",stdout);
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    JavaScript监控当前cpu使用状况
    JavaScript面向对象编程深入分析(1)
    学习Javascript闭包(Closure)
    JavaScript面向对象编程深入分析(3)
    JavaScript面向对象编程深入分析(2)
    浏览器的标准模式和怪异模式
    js实现简单网速测试方法
    浏览器的两种模式quirks mode 和strict mode
    详解Javascript 中的this指针
    virtualbox安装centos 6.4 server 网络连接问题
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7920813.html
Copyright © 2011-2022 走看看