P6143 [USACO20FEB]Equilateral Triangles P——几何+二维前缀和
程序员文章站
2022-07-13 18:06:49
...
题目来源: P6143 [USACO20FEB]Equilateral Triangles P.
先手工做这个题目,感觉要枚举,和每行的斜线有关系,但是想不出来。只好开始我的题解大法。本题的曼哈顿距离经过转换,可以发现下面的关系:
设ABC可以构成等边三角形则AB=AC=BC,变幻后,
AB=OA+OB,AC=OA+OC,BC=OB+OC,
因此可以得出结论OA=OB=OC,且OA垂直OB;
可以和AB匹配的点C有什么特点?设ABC1C3可以构成正方形,那么C1-C3区间内的点可以都可以与AB构成等边三角形,因此我们枚举A点O(n*n),再枚举B点O(n),然后用二维前缀和找出可以匹配的C点,当然C点所在的边有两条,AB上方和下方个一条。
同理,还需要换个方向,找出BC1的平行边所对应的点。
需要注意的是,如果有ABC三点构成正方形的四个顶点,会重复统计,因此第二个方向统计的时候需要去除端点,避免重复计数。
如何统计C点呢?
我们首先要将题目所给正方形旋转45度,将每条对角线转换为水平存储,注意保持原坐标的绝对值差值不变。
统计好后,维护其二维前缀和。
参考代码:(COPY from luogu)
//tj
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=305,M=610;
int n,m,a[M][M],b[M][M];//b统计对角线上的牛的数目
char s;
ll ans;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){//把对角线旋转45度,变成矩形。
cin>>s;
if(s=='*')a[i+j-1][i-j+n]=1;
// cout<<i<<" "<<j<<","<<i+j-1<<" "<<i-j+n<<endl;
}
m=2*n;
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
b[i][j]=a[i][j]+b[i][j-1];//统计每行的前缀和
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
if(a[i][j])//一个顶点
for(int k=j+1;k<=m;k++)//以j,k为顶点的正方形
if(a[i][k]){//另一个顶点,他们的距离是k-j ,再统计他们距离为k-j(上方和下方)的正方形平行线区域有多少个点
if(i-(k-j)>=1)
ans+=b[i-k+j][k]-b[i-k+j][j-1];
if(i+k-j<=m)
ans+=b[i+k-j][k]-b[i+k-j][j-1];
}
for (int i = 1; i <= m; i++)//重新统计竖直方向前缀和。
for (int j = 1; j <= m; j++)
b[i][j] = a[i][j] + b[i-1][j];
for (int i = 1; i <= m; i++)//换个方向,以j,k为顶点,但是正方形练习边界上的三个点已经统计过,这次不再统计,统计的区间是
for (int j = 1; j <= m; j++)//所以后面统计的区间是 k-1,j+1
if (a[j][i])
for (int k = j + 1; k <=m; k++)
if (a[k][i]) {
if (i - (k - j) >= 1)
ans += b[k-1][i-(k-j)] - b[j][i-(k-j)];
if (i + (k - j) <=m)
ans += b[k-1][i+(k-j)] - b[j][i+(k-j)];
}
cout<<ans<<endl;
}
上一篇: 成大事的九种手段
下一篇: JAVA基础语法01