【NOIP2017】列队

题面

分析

知名数据结构题。

今天拿给我做我还是会毫不犹豫地敲个50的暴力吧,因为细节其实是很多的。

考虑维护n+1棵线段树,前n棵维护n排的m-1个人,最后一棵维护最后一列的n个人

每次坐标为(x,y)的一个人离开队伍,相当于把这个人取出来放到最后一列的最后一个位置。

然后就这样就完了吗?原来第x行的最后一个位置,这时应该成为了倒数第二个位置,换言之,它本来不在第x棵线段树的维护范围内,现在它进入了,不能忘记把它加进去。

显然数组开不下,考虑动态开点,初始每棵树仅一个结点,其权值表示有多少个点。在我们查询或者插入需要访问用到一个结点时,再加入这个结点,并给它赋上信息。

代码

  1. #include<bits/stdc++.h>  
  2. using namespace std;  
  3. #define M 300030  
  4. #define N 8000080  
  5. #define mid (l+r>>1)  
  6. #define ll long long  
  7. int n,m,q,w,cnt,now;  
  8. int root[M],t[N],lc[N],rc[N],pos[N];  
  9. ll out,id[N];  
  10. inline int cal(int l,int r)  
  11. {//查询这段区间的结点数   
  12.     if(now<=n)  
  13.     {  
  14.         if(r<m)return r-l+1;  
  15.         else if(l<m) return m-1-l+1;  
  16.         else return 0;  
  17.     }  
  18.     else  
  19.     {  
  20.         if(r<=n)return r-l+1;  
  21.         else if(l<=n) return n-l+1;  
  22.         else return 0;  
  23.     }  
  24. }  
  25. inline void query(int &p,int l,int r)  
  26. {  
  27.       
  28.     if(!p)p=++cnt,t[p]=cal(l,r);  
  29.     if(l==r)  
  30.     {  
  31.         t[p]=0;  
  32.         if(!id[p])out=(now<=n)?(ll)(now-1)*(ll)m+l:(ll)m*(ll)l;  
  33.         else out=id[p];  
  34.         return;  
  35.     }  
  36.     else  
  37.     {  
  38.         t[p]--;//删除一个结点   
  39.         int left=lc[p]?t[lc[p]]:cal(l,mid);//个数是否达到要查的数量    
  40.         if(w<=left)query(lc[p],l,mid);//没达到,往左找   
  41.         else w-=left,query(rc[p],mid+1,r);//超过了,在右边找   
  42.     }  
  43. }  
  44.   
  45. inline void update(int &p,int l,int r)  
  46. {  
  47.     if(!p)p=++cnt,t[p]=cal(l,r);  
  48.     if(l==r){t[p]=1;id[p]=out;return ;}  
  49.     ++t[p];//加入一个结点   
  50.     if(w<=mid)update(lc[p],l,mid);  
  51.     else update(rc[p],mid+1,r);  
  52. }  
  53.   
  54. int main()  
  55. {  
  56.     scanf("%d%d%d",&n,&m,&q);  
  57.     for(int i=1;i<=n;i++)root[i]=++cnt,t[i]=m-1;  
  58.     root[n+1]=++cnt;t[n+1]=n;  
  59.     for(int i=1;i<=q;i++)  
  60.     {  
  61.         int a,b;ll tmp;  
  62.           
  63.         scanf("%d%d",&a,&b);  
  64.         if(b<m)  
  65.         {  
  66.             now=a,w=b;  
  67.             query(root[now],1,m-1+q);//找到出队的点   
  68.             printf("%lld ",out);  
  69.             tmp=out,now=n+1;w=a;  
  70.             query(root[now],1,n+q); //从最后一列中找x行的最后一个点   
  71.             now=a,pos[now]++,w=m-1+pos[now];  
  72.             update(root[now],1,m-1+q);//把它归入第x棵线段树管理范畴  
  73.             now=n+1,pos[now]++,w=n+pos[now],out=tmp;  
  74.             update(root[now],1,n+q);//把最后一列最后一个点更新为出队的点   
  75.         }  
  76.         else  
  77.         {  
  78.             now=n+1,w=a;  
  79.             query(root[now],1,n+q);//本来就在最后一列,省去维护x行最后一列的操作   
  80.             printf("%lld ",out);  
  81.             pos[now]++,w=n+pos[now];  
  82.             update(root[now],1,n+q);  
  83.         }  
  84.     }  
  85.     return 0;  
  86. }  
原文地址:https://www.cnblogs.com/NSD-email0820/p/9828518.html