BZOJ3543: [ONTAK2010]Garden
http://www.lydsy.com/JudgeOnline/problem.php?id=3543
首先明确两点,对于一个平行于坐标轴的正方形,只需要确定的一条边就可以,比如说下边或者右边。还有一个结论:n个点的二维点集的平行坐标轴的正方形个数是O(n√n)的。
所以我们大概就明白了这题是需要暴力统计的。
将横坐标相同的点放进同一个集合,点数>√n的称为大集合,否则是小集合。将读入的点丢进hash里。
枚举每个集合:
对于大集合,易知不超过√n个,每个就枚举其左侧的每个点,那么就确定了下边界,hash判定一下另外三个点的存在性即可。复杂度O(n√n)。
对于小集合,暴力枚举集合内的点对来确定右边,hash判定。总复杂度不超过O(n√n)。
#include<bits/stdc++.h> #include<tr1/unordered_set> using namespace std; const int maxn=100010,maxy=2000010; struct point{ int x,y; point(): x(0),y(0) { } point(int x_,int y_): x(x_),y(y_) { } bool operator==(const point &b)const{ return x==b.x && y==b.y; } }a[maxn]; struct hasher{ long long operator()(const point &t)const{ const long long seed=23333; return t.x*seed+t.y; } }; std::tr1::unordered_set<point,hasher> t; vector<int> heavy; int l[maxy],r[maxy]; bool flag[maxy]; int n,S; inline bool cmp_y(const point &a,const point &b){ return a.y<b.y || (a.y==b.y && a.x<b.x); } void init(){ scanf("%d",&n),S=sqrt(n)*2; t.clear(),heavy.clear(); for(int i=1;i<=n;++i){ scanf("%d%d",&a[i].x,&a[i].y); a[i].y+=1e6,t.insert(a[i]); } sort(a+1,a+n+1,cmp_y); } void solve(){ memset(l,-1,sizeof(l)); memset(r,-1,sizeof(r)); memset(flag,false,sizeof(flag)); for(int u=1,v=1;u<=n;u=v){ while(v<=n && a[v].y==a[u].y) ++v; l[a[u].y]=u,r[a[u].y]=v; if(v-u>S) heavy.push_back(a[u].y),flag[a[u].y]=true; } int ans=0; for(int i=1,x,y,d;i<=n;++i){ x=a[i].x,y=a[i].y; if(flag[y]){ for(int j=0;j<heavy.size();++j){ d=heavy[j]-y; if(d<=0) continue; if(t.count(point(x,y+d)) && t.count(point(x+d,y)) && t.count(point(x+d,y+d))) ++ans; } } else{ for(int j=i+1;j<r[y];++j){ d=a[j].x-x; if(!d) continue; if(t.count(point(x,y+d)) && t.count(point(x+d,y+d))) ++ans; if(y-d>=0 && flag[y-d] && t.count(point(x,y-d)) && t.count(point(x+d,y-d))) ++ans; } } } printf("%d\n",ans); } int main(){ init(); solve(); return 0; }