C90 动态开点线段树 P3960 [NOIP2017 提高组] 列队

视频链接:C90 动态开点线段树 P3960 [NOIP2017 提高组] 列队_哔哩哔哩_bilibili

 

 

 

Luogu P3960 [NOIP2017 提高组] 列队

// 用vector开n+1个新队列,动态开n+1颗线段树 O(nlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define LL long long
#define mid ((l+r)>>1)
const int N=300005,M=N*40;
int n,m,q,x,y,tot,mx;
int rt[N],ls[M],rs[M],sum[M]; //权值线段树
vector<LL> qu[N]; //新队列
LL mcol,num;      //第m列数,离队数

void change(int &x,int l,int r,int p){ //点修
  if(!x) x=++tot; //开点
  sum[x]++;       //从根到p的节点均+1
  if(l==r) return;
  if(p<=mid) change(ls[x],l,mid,p);
  else change(rs[x],mid+1,r,p);
}
int query(int x,int l,int r,int p){ //点查
  if(l==r) return l;        //返回p的下标位置
  int t=mid-l+1-sum[ls[x]]; //左区间的空穴数
  if(p<=t) return query(ls[x],l,mid,p);
  else return query(rs[x],mid+1,r,p-t);
}
LL work1(int x,LL y){ //num在第m列
  int row=query(rt[0],1,mx,x); //x在第m列的行下标
  change(rt[0],1,mx,row);
  if(row<=n) mcol=1ll*row*m;   //mcol来自原队
  else mcol=qu[0][row-n-1];    //mcol来自新队qu[0]
  qu[0].push_back(y?y:mcol);   //num压入新队qu[0]
  return mcol;                 //返回mcol
}
LL work2(int x,int y){ //num不在第m列
  int col=query(rt[x],1,mx,y);   //y在第x行的列下标
  change(rt[x],1,mx,col);
  if(col<m) num=1ll*(x-1)*m+col; //num来自原队
  else num=qu[x][col-m];         //num来自新队qu[x]
  qu[x].push_back(work1(x,num)); //mcol压入新队qu[x]
  return num;                    //返回num
}
int main(){
  scanf("%d%d%d",&n,&m,&q); mx=max(n,m)+q;
  while(q--){
    scanf("%d%d",&x,&y);
    printf("%lld\n",y==m?work1(x,0):work2(x,y));
  } 
}

 

posted @ 2024-01-12 16:33  董晓  阅读(217)  评论(0编辑  收藏  举报