C89 树状数组+二分 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; } }