题目链接: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;
}