C89 树状数组+二分 P3960 [NOIP2017 提高组] 列队

视频链接:

 

 

 

Luogu P3960 [NOIP2017 提高组] 列队

// 树状数组+二分 O(N*logN*logN)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define int long long
const int N=300005;
int read(){
  int x=0,f=1;char c=getchar();
  while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
  while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
  return x*f;
}
int n,m,q,mx,x[N],y[N];
struct Q{
  int y,iq;        //列下标,第i个询问
};
vector<Q> v[N];    //存储询问信息
int col[N];        //存储查询的列下标
int s[N*2];        //权值树状数组
vector<int> qu[N]; //新队列
int mcol,num;      //第m列数,离队数

void change(int x,int k){ //向后修
  while(x<=mx) s[x]+=k, x+=x&-x;
}
int query(int x){ //向前查
  int t=0;
  while(x) t+=s[x], x-=x&-x;
  return t;
}
int Bsearch(int x){ //二分查找≥x的最小值
  int l=-1,row=mx+1,mid;
  while(l+1<row){
    mid=(l+row)>>1;
    query(mid)>=x?row=mid:l=mid;
  }
  return row;
}
signed main(){
  n=read();m=read();q=read(); mx=max(m,n)+q; //树节点个数
  for(int i=1;i<=q;i++){
    x[i]=read(); y[i]=read();
    if(y[i]!=m)              //不在第m列,则按行存储询问信息
      v[x[i]].push_back({y[i],i});
  }
  for(int i=1;i<=mx;i++) change(i,1); //树节点初始为1
  for(int i=1;i<=n;i++){        //逐行处理
    for(Q t:v[i]){
      col[t.iq]=Bsearch(t.y);   //树上二分,存储查询的列下标
      change(col[t.iq],-1);     //树上删除col的贡献
    }
    for(Q t:v[i])
      change(col[t.iq],1);      //还原树状数组,以免影响下一行
  }
  for(int i=1,row;i<=q;i++){ //枚举询问
    row=Bsearch(x[i]);          //树上二分,找出查询的行下标
    change(row,-1);             //树上删除row的贡献
    if(row<=n) mcol=row*m;      //mcol来自原队列
    else mcol=qu[0][row-n-1];   //mcol来自新队列
    if(y[i]==m) num=mcol;       //离队数是mcol
    else{                       //离队数不是mcol
      qu[x[i]].push_back(mcol); //mcol压入新队列
      if(col[i]<m) num=(x[i]-1)*m+col[i]; //num来自原队列
      else num=qu[x[i]][col[i]-m];        //num来自新队列
    }
    qu[0].push_back(num);                 //num压入新队列
    cout<<num<<endl;
  }
}

  

posted @ 2024-01-04 21:23  董晓  阅读(226)  评论(0编辑  收藏  举报