欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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;
P6143 [USACO20FEB]Equilateral Triangles P——几何+二维前缀和
可以和AB匹配的点C有什么特点?设ABC1C3可以构成正方形,那么C1-C3区间内的点可以都可以与AB构成等边三角形,因此我们枚举A点O(n*n),再枚举B点O(n),然后用二维前缀和找出可以匹配的C点,当然C点所在的边有两条,AB上方和下方个一条。
同理,还需要换个方向,找出BC1的平行边所对应的点。
需要注意的是,如果有ABC三点构成正方形的四个顶点,会重复统计,因此第二个方向统计的时候需要去除端点,避免重复计数。
P6143 [USACO20FEB]Equilateral Triangles P——几何+二维前缀和
如何统计C点呢?
我们首先要将题目所给正方形旋转45度,将每条对角线转换为水平存储,注意保持原坐标的绝对值差值不变。
统计好后,维护其二维前缀和。
P6143 [USACO20FEB]Equilateral Triangles P——几何+二维前缀和
参考代码:(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;	

}