题解 P8228 【「Wdoi-5」模块化核熔炉】
题解
本题要求我们在一个巨大的六边形网格中执行正六边形范围的加法。但是正六边形加法并不容易。这里先考虑在一个正方形网格当中执行矩形范围的加法。
为了方便,这些图从左往右分别称为
利用「差分」和「前缀和」的思路。假设我们现在需要进行矩形加法。如
此时发现,在
实际操作的时候,只需要对这四个格子进行操作,可以得到
这么做有个很好的性质:我们可以把若干次区间加操作在
但是我们要做的是正六边形里的加法啊。但是不着急。因为我们可以发现,在一个正六边形阵列里,可以按照某个方向进行变换,将一个平行四边形区域转化为矩形区域:
因此,这里考虑将给我们的坐标进行坐标转换。
具体而言,我们新建这样两个坐标系。
按照原点的方位,我们分别将它们称呼为左系和右系。先讨论如何将题目给定的坐标系(称呼为标准系)的坐标转换为左系和右系当中点的坐标。假设给定的坐标为
- 容易发现,在标准系当中,每向
x 方向走一格会导致左系的x 增加1 ,y 减小1 ;每向y 方向走一格会导致左系的y 增加1 ;每向z 方向走一格会导致左系的x 减小1 。考虑到标准系中原点的坐标在左系中为(n,n) ,那么标准系中的坐标(x_0,y_0,z_0) 转换为左系坐标就是(x_0-z_0+n,y_0-x_0+n) 。 - 同时发现,在标准系当中,每向
x 方向走一格会导致右系的x 增加1 ;每向y 方向走一格会导致右系的y 增加1 ;每向z 方向走一格会导致右系的x 减小1 ,y 减小1 。考虑到标准系中原点的坐标在右系中为(n,n) ,那么标准系中的坐标(x_0,y_0,z_0) 转换为左系坐标就是(x_0-z_0+n,y_0-z_0+n) 。
这张图是对一个右系进行矩形变换后的结果。容易发现,在原图当中一个向右倾斜的平行四边形区域,可以被被转化为右系当中的一个矩形。同理,我们可以对左系进行矩形变换:
因此,我们可以将一个六边形网格图中,一个向左倾斜的平行四边形区域的区间加,转化为左系所对应的那个矩形里的一个矩形加。
知道这些,这条题目就非常简单了。现在假设我们需要对原图当中这样一个大的正六边形区域执行加法操作:
那么它可以被转换为这样四个图形之间的拼接:
其中,红色部分代表的是正值,蓝色部分代表的是负值。它可以被转化为两个左系当中的矩形加,以及两个右系当中的矩形加。
记得处理一下矩阵的边界超出左系/右系的情况,那么这题就做完了。
参考代码
#include<bits/stdc++.h>
#define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i)
#define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i)
using namespace std;
typedef long long i64;
const int INF =2147483647;
int qread(){
int w=1,c,ret;
while((c=getchar())> '9'||c< '0') w=(c=='-'?-1:1); ret=c-'0';
while((c=getchar())>='0'&&c<='9') ret=ret*10+c-'0';
return ret*w;
}
const int MAXN=800+3;
int n,m,k;
void cnv1(int a,int b,int c,int &x,int &y){ //左系
x=a-c+n,y=b-a+n;
}
void cnv2(int a,int b,int c,int &x,int &y){ //右系
x=a-c+n,y=b-c+n;
}
int A[MAXN*2][MAXN*2],B[MAXN*2][MAXN*2];
int main(){
n=qread(),m=qread(),k=2*n-1;
up(1,m,i){
int x=qread(),y=qread(),z=qread(),r=qread(),w=qread();
int u,v;
cnv1(x,y,z,u,v);
A[u][min(k,v+r-1)]-=w,A[max(1,u-r+1)][min(k,v+r-1)]+=w;
A[u][max(0,v-r )]-=w,A[min(k+1,u+r)][max(0,v-r )]+=w;
cnv2(x,y,z,u,v);
B[u][min(k,v+r-1)]+=w,B[min(k+1,u+r)][min(k,v+r-1)]-=w;
B[u][max(0,v-r )]+=w,B[max(1,u-r+1)][max(0,v-r )]-=w;
}
up(1,k,i) up(1,k,j) A[i][j]+=A[i-1][j];
up(1,k,i) up(1,k,j) B[i][j]+=B[i-1][j];
up(1,k,i) dn(k,1,j) A[i][j]+=A[i][j+1];
up(1,k,i) dn(k,1,j) B[i][j]+=B[i][j+1];
up(-n+1,n-1,i) dn(n-1,-n+1+abs(i),j){
int x=0,y=j,z=0,u,v,w=0; if(i<0) z=-i; else x=i;
cnv1(x,y,z,u,v),w+=A[u][v];
cnv2(x,y,z,u,v),w+=B[u][v];
printf("%d ",w);
}
return 0;
}