[noip2017]列队

https://www.zybuluo.com/ysner/note/1322438

题面

无人不知,无人不晓

解析

想想如何简化题目中的操作。
一个人到\((n,m)\),暴力模拟的复杂度是\(O(n+m)\)
考虑节省掉移动整行整列的复杂度。

实际上,不如让一个人移走后留下的位置空着。
这样一行最多只有\(6*10^5\),是可以维护的。

于是用\(n\)棵线段树维护一下每一行中,区间内有多少个空位置。
这样就可以查出一行的第\(k\)个在哪里。

当然,如果每行线段树都开全,空间复杂度\(O(nm)\)开不下。
所以需要动态开点。

注意到只有\(n\)行和第\(m\)列有可能新加入数。
可以用\(vector\)存一下,每行新加入了什么数(第\(m\)列不包含在内)。
还有一个\(vector\)存第\(m\)列新加入的数。

另外,删掉\(vector\)中的数也是\(O(n)\)级别的。所以每行的线段树也要维护\(vector\)中的空位置。

这么以来,操作就转化为在数据结构的基础上模拟了。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define re register
#define il inline
#define pb push_back
#define fp(i,a,b) for(re int i=a;i<=b;++i)
#define fq(i,a,b) for(re int i=a;i>=b;--i)
using namespace std;
const int N=3e5+100;
int n,m,q,t[N*100],tot,rt[N*100],tim,ls[N*100],rs[N*100];
vector<ll>V[N];
il ll gi()
{
  re ll x=0,t=1;
  re char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
il void Inherit(re int &x)
{
  rt[++tim]=rt[x];t[tim]=t[x];ls[tim]=ls[x];rs[tim]=rs[x];x=tim;
}
il int Query(re int &x,re int l,re int r,re int k)
{
  Inherit(x);
  if(l==r) {t[x]=1;return l;}
  re int mid=l+r>>1,sz=mid-l+1-t[ls[x]],ans;
  if(k<=sz) ans=Query(ls[x],l,mid,k);
  else ans=Query(rs[x],mid+1,r,k-sz);
  t[x]=t[ls[x]]+t[rs[x]];
  return ans;
}
il ll find(re int i,re int k)
{
  re ll ans=Query(rt[i],1,tot,k),l=(i<=n?m-1:n);
  if(ans<=l) return i<=n?1ll*(i-1)*m+ans:ans*m;
  else return V[i][ans-l-1];
}
int main()
{
  n=gi();m=gi();q=gi();tot=max(n,m)+q;
  fp(i,1,n+1) rt[i]=++tim;
  fp(i,1,q)
    {
      re int x=gi(),y=gi();re ll p1,p2;
      if(y^m)
    {
      p1=find(x,y);V[n+1].pb(p1);//a new member on lie m
      p2=find(n+1,x);V[x].pb(p2);//new one ->the n_th line
    }
      else p1=find(n+1,x),V[n+1].pb(p1);
      printf("%lld\n",p1);
    }
  return 0;
}
posted @ 2018-10-25 21:11  小蒟蒻ysn  阅读(148)  评论(0编辑  收藏  举报