小a的轰炸游戏(二维差分)

题目链接:https://ac.nowcoder.com/acm/contest/317/E

题意:我就直接根据题目给的样例来表述题意吧。
样例如下:
输入:
4 5 4
1 2 2 1
1 3 3 5
1 3 2 3
2 2 4 3
输出:
2

首先4和5表示初始矩形的长和宽,4表示询问次数
接下来4行,第一个输入opt,表示飞机的类型
如果opt==1,那么轰炸范围为整个菱形,否则,为菱形的上半部分,之后两个数位x,y,表示菱形的中心坐标,之后输入的l为菱形对角线的长度。最后输出所有格子被轰炸次数的异或和。
如果我解释的不清楚的话可以看原图的说明,那个讲的很详细。

思路:这一题因为数据范围很小,所以可以考虑二维差分。那么我们就可以把问题转化到一维差分的问题。那对每一行进行一维差分就可以啦。以 l == 5为例,只要我们可以做到如下图这样就可以啦
在这里插入图片描述
但是呢,我们直接暴力跑一遍是一定会超时的,所以我们要想别的办法。
起初我怎么也没想到,后来看了大佬的题解才知道可以用四个标记数组来实现。
如图所示:

在这里插入图片描述

图中的绿色区域是要进行区间操作的菱形,红色圈是+1操作,蓝色圈是减一操作,箭头表示数组传递方向,这样,按照图示方向传递数组后可以得到:
在这里插入图片描述
之后这个二维差分就转化为了一位差分,问题迎刃而解啦。

还有一个需要注意的是菱形可能越界,那我们可以加一个偏移量,最后只统计n*m范围内的就好

具体操作请看代码。
AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int maxn=3010;
const int L=1000;//偏移量

int a[maxn][maxn],b[maxn][maxn],c[maxn][maxn],d[maxn][maxn];
int ans[maxn][maxn];

//对菱形上半部分进行操作
void up(int x,int y,int l)
{
    a[x-l/2][y]++,a[x+1][y-l/2-1]--;
    b[x+1][y+l/2+2]++,b[x-l/2][y+1]--;
}

//对菱形下半部分进行操作
void down(int x,int y,int l)
{
    c[x+1][y-l/2+1]++,c[x+l/2+1][y+1]--;
    d[x+l/2+1][y]++,d[x+1][y+l/2]--;
}

int main()
{
    int n,m,q;
    while(~scanf("%d%d%d",&n,&m,&q))
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        memset(d,0,sizeof(d));
        memset(ans,0,sizeof(ans));
        while(q--)
        {
            int opt,x,y,l;
            scanf("%d%d%d%d",&opt,&x,&y,&l);
            x+=L,y+=L;
            up(x,y,l);
            if(opt==1) down(x,y,l);
        }

        for(int i=1;i<=n+L*2;i++)
        {
            for(int j=1;j<=m+L*2;j++)
            {
                a[i][j]+=a[i-1][j+1];
                b[i][j]+=b[i-1][j-1];
                c[i][j]+=c[i-1][j-1];
                d[i][j]+=d[i-1][j+1];
            }
        }

        for(int i=1;i<=2*L+n;i++)
        {
            for(int j=1;j<=2*L+m;j++)
            {
                ans[i][j]+=ans[i][j-1]+a[i][j]+b[i][j]+c[i][j]+d[i][j];
            }
        }

        int ANS=0;
        for(int i=L+1;i<=L+n;i++)
        {
            for(int j=L+1;j<=L+m;j++)
            {
                ANS^=ans[i][j];
            }
        }
        printf("%d\n",ANS);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值