P6143 [USACO20FEB]Equilateral Triangles P——几何+二维前缀和

题目来源: 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;	

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章