C90 动态开点线段树 P3960 [NOIP2017 提高组] 列队
视频链接:C90 动态开点线段树 P3960 [NOIP2017 提高组] 列队_哔哩哔哩_bilibili
// 用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)); } }