ICPC 2018 焦作 C题 Supreme Command

15 篇文章 0 订阅

​​在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

题意

给出一个 n × n n×n n×n的棋盘,保证每行每列只有一个棋子,给出多种操作,将所有棋子先上/下/左/右移动 k k k步(遇到边界则停止移动),提问某一个棋子的现在的位置或问当前有多少对棋子是在一个格子里的。

题解

棋盘的两个方向可以分别考虑,我们现在只考虑水平方向
用区间 [ l , r ] [l,r] [l,r]表示对于初始棋盘来说,水平坐标在 [ l , r ] [l,r] [l,r]范围内的棋子是没有触碰到边界的
比如样例中的棋盘,向左移动2格后,只有初始坐标在 [ 3 , 4 ] [3,4] [3,4]的棋子是没有碰到边界的,但真实情况是坐标集中在区间 [ 1 , 2 ] [1,2] [1,2],所以还要记录一个变量 t t t表示两个坐标的偏差值
为什么这样做?
这样是为了询问棋子坐标的操作,当水平坐标 x &lt; l x&lt;l x<l时,说明在边界上,那么 l − t l-t lt就是真实的坐标,同理, x &gt; r x&gt;r x>r时为 r − t r-t rt l &lt; = x &lt; = r l&lt;=x&lt;=r l<=x<=r时为 x − t x-t xt
[ l , r ] [l,r] [l,r]的维护方法就留给读者自己考虑(代码中有提示)
接下来是询问在同一格子的对数
棋子的整体移动可以等效为棋盘边界的反方向移动,那么很容易观察到会有多个棋子的格子只能在边界的四个角上,这样用4个变量维护4个角的棋子个数就可以了
维护方法
左上角的为 l i f t   a n d   u p lift\ and\ up lift and up,所以变量为 l u lu lu, l d   r u   r d ld\ ru\ rd ld ru rd也是同理
上边界和下边界有两条扫描线
当区间 [ u , d ] [u,d] [u,d]更新时,看边界经过的点的坐标x与区间 [ l , r ] [l,r] [l,r]的关系

关系操作
x &lt; l x&lt;l x<l左边界已经扫过,说明这个点已经在角格子了,对应变量直接加1
l &lt; x &lt; r l&lt;x&lt;r l<x<r说明该点不在边界,则 m a r k [ x ] = 1 mark[x]=1 mark[x]=1,标记一下,更新 [ l , r ] [l,r] [l,r]时加到对应变量里
r &lt; x r&lt;x r<x右边界已经扫过,说明这个点已经在角格子了,对应变量直接加1

当区间 [ l , r ] [l,r] [l,r]更新时,如果 m a r k [ ] = = 1 mark[]==1 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();
            }
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值