题意
给出一个 n×n的棋盘,保证每行每列只有一个棋子,给出多种操作,将所有棋子先上/下/左/右移动 k步(遇到边界则停止移动),提问某一个棋子的现在的位置或问当前有多少对棋子是在一个格子里的。
题解
棋盘的两个方向可以分别考虑,我们现在只考虑水平方向
用区间 [l,r]表示对于初始棋盘来说,水平坐标在 [l,r]范围内的棋子是没有触碰到边界的
比如样例中的棋盘,向左移动2格后,只有初始坐标在 [3,4]的棋子是没有碰到边界的,但真实情况是坐标集中在区间 [1,2],所以还要记录一个变量 t表示两个坐标的偏差值
为什么这样做?
这样是为了询问棋子坐标的操作,当水平坐标 x<l时,说明在边界上,那么 l−t就是真实的坐标,同理, x>r时为 r−t, l<=x<=r时为 x−t
[l,r]的维护方法就留给读者自己考虑(代码中有提示)
接下来是询问在同一格子的对数
棋子的整体移动可以等效为棋盘边界的反方向移动,那么很容易观察到会有多个棋子的格子只能在边界的四个角上,这样用4个变量维护4个角的棋子个数就可以了
维护方法
左上角的为 lift and up,所以变量为 lu, ld ru rd也是同理
上边界和下边界有两条扫描线
当区间 [u,d]更新时,看边界经过的点的坐标x与区间 [l,r]的关系
关系 | 操作 |
---|---|
x<l | 左边界已经扫过,说明这个点已经在角格子了,对应变量直接加1 |
l<x<r | 说明该点不在边界,则 mark[x]=1,标记一下,更新 [l,r]时加到对应变量里 |
r<x | 右边界已经扫过,说明这个点已经在角格子了,对应变量直接加1 |
当区间 [l,r]更新时,如果 mark[]==1,说明之前已经遇到上下边界,现在左右边界又遇到了,那么肯定以后都在角格子里了,对应计数的变量加1
代码:
#include<bits/stdc++.h>
#define N 300010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define P 998244353
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
int dx,dy,n,m,l,r,u,d,p;
int x[N],y[N],a[N],b[N],c[N],lu,ld,ru,rd;
LL C[N];
void workL()
{
//real [l-dy,r-dy]
//if l-dy<1 update l, else update dy
int t=l-dy-p;
if (t>=1) dy+=p;else
{
int pl=l;
l-=t-1;
if (l>r) l=r;
dy=l-1;
for (int i=pl+1;i<=l-(l==r);i++) lu+=a[i],ld+=b[i];
}
}
void workR()
{
//if r-dy>n update r, else update dy
int t=r-dy+p-n;
if (t>0)
{
int pr=r;
r-=t;
if (l>r) r=l;
dy=r-n;
for (int i=pr-1;i>=r+(l==r);i--) ru+=a[i],rd+=b[i];
}else dy-=p;
}
void workU()
{
int t=u-dx-p;
if (t>=1) dx+=p;else
{
int pu=u;
u-=t-1;
if (u>d) u=d;
dx=u-1;
for (int i=pu+1;i<=u-(d==u);i++){
if (c[i]>=r) ru++;else
if (c[i]>l) a[c[i]]=1;else lu++;
}
}
}
void workD()
{
int t=d-dx+p-n;
if (t>0)
{
int pd=d;
d-=t;
if (u>d) d=u;
dx=d-n;
for (int i=pd-1;i>=d+(d==u);i--){
if (c[i]>=r) rd++;else
if (c[i]>l) b[c[i]]=1;else ld++;
}
}else dx-=p;
}
void cal()
{
if (l==r && u==d) {
printf("%lld\n",C[n]);
}else
if (l==r){
printf("%lld\n",C[lu+ru]+C[ld+rd]);
}else
if (u==d){
printf("%lld\n",C[lu+ld]+C[ru+rd]);
}else
printf("%lld\n",C[lu]+C[ru]+C[ld]+C[rd]);
}
void query()
{
int ax=max(min(x[p],d),u),ay=max(min(y[p],r),l);
ax-=dx; ay-=dy;
printf("%d %d\n",ax,ay);
}
int main()
{
for (int i=0;i<N;i++) C[i]=(LL) i*(i-1)/2;
int T;
sc(T);
while(T--)
{
scc(n,m);
dx=dy=0;l=u=0;r=d=n+1;
for (int i=1;i<=n;i++) scc(x[i],y[i]),a[i]=b[i]=0,c[x[i]]=y[i];
lu=ld=ru=rd=0;
while(m--)
{
char x[4];
scanf("%s",x);
if (x[0]=='!') cal(); else
{
sc(p);
if (x[0]=='L') workL();else
if (x[0]=='R') workR();else
if (x[0]=='U') workU();else
if (x[0]=='D') workD();else
if (x[0]=='?') query();
}
}
}
}