考虑n=1的时候:
维护一个数列,每次删除一个数,再数列的结尾加上一个数,求第K大
这是splay,treap模板题
当然,不单单可以用他们来做
来可以用老少皆宜的线段树来做
维护01数列表示那个数是否存在,提前开出空间
删掉一个数标记成0,加上一个数常规加,求第K大就是线段树上二分
也可以用树状数组来做
与线段树操作一样,求第K大就是树状数组上倍增
当n!=1时
维护n+1个数列,每次操作如上
开n+1棵splay或者treap,或者动态开点线段树
不能开点,就意味着必须记删掉哪些数,注意开long long
当然,我只写了老少皆宜的线段树啦
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
int read()
{
int ret=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9')
{
ret=(ret<<1)+(ret<<3)+ch-'0';
ch=getchar();
}
return ret;
}
void write(ll x)
{
if(x/10) write(x/10);
putchar(x%10+48);
}
const int N=1e6+5,M=1e7+5;
int n,m,r,T,x,y,cnt,rt[N],lst[N],siz[M],ls[M],rs[M];
ll ans,c[M];
ll find(int &p,int l,int r,int x)
{
if(!p) p=++cnt;
if(l==r)
return (c[p]==0)?-l:c[p];
int mid=l+r>>1;
if(mid-l+1-siz[ls[p]]>=x)
return find(ls[p],l,mid,x);
else
return find(rs[p],mid+1,r,x-(mid-l+1-siz[ls[p]]));
}
void del(int &p,int l,int r,int x)
{
if(!p) p=++cnt;
siz[p]++;
if(l==r)
{
c[p]=-1;
return;
}
int mid=l+r>>1;
if(mid-l+1-siz[ls[p]]>=x)
del(ls[p],l,mid,x);
else
del(rs[p],mid+1,r,x-(mid-l+1-siz[ls[p]]));
}
void add(int &p,int l,int r,int x,ll y)
{
if(!p) p=++cnt;
if(l==r)
{
c[p]=y;
return;
}
int mid=l+r>>1;
if(x<=mid)
add(ls[p],l,mid,x,y);
else
add(rs[p],mid+1,r,x,y);
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
n=read(),m=read(),T=read();
r=max(n,m)+T;
for(int i=1;i<=n;i++)
lst[i]=m;
lst[n+1]=n+1;
while(T--)
{
x=read(),y=read();
if(y!=m)
{
ans=find(rt[x],1,r,y);
if(ans<0) ans=((ll)x-1)*m-ans;
del(rt[x],1,r,y);
ll t=find(rt[n+1],1,r,x);
if(t<0) t=-t*m;
add(rt[x],1,r,lst[x],t);
lst[x]++;
del(rt[n+1],1,r,x);
add(rt[n+1],1,r,lst[n+1],ans);
lst[n+1]++;
}else
{
ans=find(rt[n+1],1,r,x);
if(ans<0) ans=-ans*m;
del(rt[n+1],1,r,x);
add(rt[n+1],1,r,lst[n+1],ans);
lst[n+1]++;
}
write(ans);
puts("");
}
return 0;
}