您的位置:首页 > 其它

BZOJ3543: [ONTAK2010]Garden

2015-08-11 09:47 253 查看
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;
}


my code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: