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;	

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