【题解】[USACO20FEB]Equilateral Triangles P
基础数数题。
曼哈顿距离不方便数点,比较套路的转化为切比雪夫距离。
那么现在要数三元组 \((i,j,k)\) 个数使得两两距离相等。
首选一下发现只用两种情况,第一种是三个点构成等腰直角三角形,第二种是三个点构成锐角三角形使得存在一条平行于坐标轴的边,且该边上的高和它的长度相等。
充分性也不难证明,首先如果三条边都不与坐标轴平行,那么反证一下发现不存在这种情况。
否则一定存在一条边与坐标轴平行,画出来如果这条边上的高与它不相等,那么也不满足条件。
最后钝角三角形显然不满足条件。所以只剩下上面两种情况。
具体实现,我们记录行前缀和,和列前缀和,然后枚举与坐标轴平行的边。求前缀和的时间复杂度是 \(\mathcal{O}(N^2)\) ,枚举边的时间复杂度是 \(\mathcal{O}(N^3)\) ,查询是 \(\mathcal{O}(1)\) 的,所以总时间复杂度为 \(\mathcal{O}(N^3)\) 。
注意实现细节,比如转化为切比雪夫距离后坐标可能为负数需要处理一下,坐标值域会扩大为原来的两倍。如果直接枚举新的值域会有 \(8\) 倍常数直接爆炸。但是转化之后的网格会变稀疏,轻微剪枝就拿到了最优解(doge
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 1005
using namespace std;
int n,a[N][N],u[N][N],v[N][N];char s[N][N];
int main(){
scanf("%d",&n);
rep(i,1,n)scanf("%s",s[i]+1);
rep(i,1,n)rep(j,1,n)if(s[i][j]=='*')a[i+j][i-j+n]=1;
int m=2*n;
rep(i,1,m)rep(j,1,m)u[i][j]=u[i][j-1]+a[i][j];
rep(j,1,m)rep(i,1,m)v[i][j]=v[i-1][j]+a[i][j];
int ans=0;
rep(k,1,m)rep(i,1,m)if(a[i][k])rep(j,i+1,m)if(a[j][k]){
if(k>j-i)ans+=v[j][k-j+i]-v[i-1][k-j+i];
if(m-k>=j-i)ans+=v[j][k+j-i]-v[i-1][k+j-i];
}
rep(k,1,m)rep(i,1,m)if(a[k][i])rep(j,i+2,m)if(a[k][j]){
if(k>j-i)ans+=u[k-j+i][j-1]-u[k-j+i][i];
if(m-k>=j-i)ans+=u[k+j-i][j-1]-u[k+j-i][i];
}
printf("%d\n",ans);
return 0;
}