传送门
解法:
a11 | a12 | a13 |
---|---|---|
a21 | a22 | a23 |
a31 | a32 | a33 |
我们发现移走一个点只会影响
该点所在行
以及最后一列
所以很容易能想到维护每一行以及最后一列
每次删两个点,加两个点
并用动态开点线段树维护
删点方法:
对线段树中每个节点处理子节点个数
记作sz[k]
递归找到要删的节点(设在线段树中位置为k)
使k位置及k位置所有父节点的sz[]-1
搜取值时就用sz[]作为判断递归条件
这样 要被删掉的点永远不可能被访问
相当于被删除了
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define inf 2000000000
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
int n,m,now,q,p;
int tot=0,o[300010],root[300010],sz[10000010],tl[10000010],tr[10000010];
ll t,val[10000010];
inline int build()
{
tot++;
tl[tot]=tr[tot]=val[tot]=0;
return tot;
}
inline int get_sz(int l,int r)
{
if(now==n+1)
{
if(r<=n) return r-l+1;
if(l<=n) return n-l+1;
return 0;
}
if(r<m) return r-l+1;
if(l<m) return m-l;
return 0;
}
ll ask_val(int &k,int l,int r,int x)
{
if(!k)
{
k=build();
sz[k]=get_sz(l,r);
if(l==r)
{
if(now==n+1) val[k]=1ll*l*m;
else val[k]=1ll*(now-1)*m+l;
}
}
sz[k]--;
if(l==r) return val[k];
int mid=(l+r)>>1;
if(x<=sz[tl[k]]||(!tl[k]&&x<=(mid-l+1))) return ask_val(tl[k],l,mid,x);
else{
if(!tl[k]) x=x-(mid-l+1);
else x=x-sz[tl[k]];
return ask_val(tr[k],mid+1,r,x);
}
}
void update(int &k,int l,int r,int x,ll v)
{
if(!k)
{
k=build();
sz[k]=get_sz(l,r);
if(l==r) val[k]=v;
}
sz[k]++;
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) update(tl[k],l,mid,x,v);
else update(tr[k],mid+1,r,x,v);
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
p=max(n,m)+q;
rep(i,1,q)
{
int x,y;
scanf("%d%d",&x,&y);
if(y==m)
{
now=n+1;
t=ask_val(root[n+1],1,p,x);
}
else
{
now=x;
t=ask_val(root[x],1,p,y);
}
printf("%lld
",t);
if(y==m)
{
o[n+1]++;
update(root[n+1],1,p,n+o[n+1],t);
}
else
{
now=n+1;
o[n+1]++;
update(root[n+1],1,p,n+o[n+1],t);
t=ask_val(root[n+1],1,p,x);
now=x;
o[x]++;
update(root[x],1,p,m-1+o[x],t);
}
}
return 0;
}