題目來源: 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;
}