BZOJ 3543: [ONTAK2010]Garden 题解

【原题】

3543: [ONTAK2010]Garden

Time Limit: 30 Sec   Memory Limit: 64 MB
Submit: 244   Solved: 84
[ Submit][ Status]

Description

给N个点,问存在多少个两边与坐标轴平行的正方形,四个顶点属于这N个点中的4个。

Input

第一行一个整数N。
接下来N行每行两个数x_i,y_i表示坐标。

Output

一行一个整数表示答案。

Sample Input

6
0 0
0 1
1 0
1 1
3 0
3 1

Sample Output

1

HINT

【数据范围】

N<=10^5,|x_i|,|y_i|<=10^6

Source


【分析】开始感觉用的是类似“边表”的方法(这个比喻不是很恰当啊)。比如对于同一个X,我想边表一样把所有Y都连在一起进行操作。但是后来想:不是还要把Y坐标排序吗?于是就想到了SET。

初始想法:枚举每一个点作为左上角的点,然后枚举X坐标上向右的Y点(作为正方形右上角的点),再枚举Y坐标上向下的X点(作为正方形左下角的点)。最后判断一下右下角的点是否在set中。

还有,直接开2个2*10^6的set会爆内存(把负数变正数),因此我先离散了一下,开了2个10^5的set。

【初始代码】

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<set>
#define A 1000000
#define N 100005
using namespace std;
typedef set<int>::iterator It;It it,r,ok1,ok2;
long long ans;
set<int>P[N],Q[N];
int n,x,y1,y2,x1,x2,aa,i,m,rex[A*2+1],rey[A*2+1],point;
inline int Read()
{
  int x=0;char ch=getchar();bool positive=1;
  for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') positive=0;
  for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
  return positive?x:-x;
}
struct arr{int x,y;}a[N+1];
bool cmp1(arr a,arr b){return a.x<b.x||a.x==b.x&&a.y<b.y;}
bool cmp2(arr a,arr b){return a.y<b.y;}
int main()
{
  freopen("p.in","r",stdin);freopen("p.out","w",stdout);
  n=Read();
  for (i=1;i<=n;i++)
    a[i].x=Read(),a[i].y=Read();
  m=0;sort(a+1,a+n+1,cmp2);a[0].y=a[1].y-1;
  for (i=1;i<=n;i++) {m+=(a[i].y!=a[i-1].y);Q[m].insert(a[i].x);rey[a[i].y+A]=m;}
  m=0;sort(a+1,a+n+1,cmp1);a[0].x=a[1].x-1;
  for (i=1;i<=n;i++) {m+=(a[i].x!=a[i-1].x);P[m].insert(a[i].y);rex[a[i].x+A]=m;}
  for (i=1;i<=n;i++)
  {
    x1=a[i].x;y1=a[i].y;point=rex[x1+A];
    for (r=P[point].upper_bound(y1);r!=P[point].end();r++)
    {
      y2=*r;aa=y2-y1;
      ok1=Q[rey[y1+A]].find(x1+aa);if (ok1==Q[rey[y1+A]].end()) continue;
      ok2=Q[rey[y2+A]].find(x1+aa);if (ok2==Q[rey[y2+A]].end()) continue;
      ans++;
    }
  }
  printf("%lld",ans);
  return 0;
}

【可惜】超时了。咨询了阳神,想法大致是对的,但很多时间耗费在枚举右上角的点和左下角的点了。可以开一个L和D指针,分别指向右边和下面。设X的set里面向左的Y有P个,Y的set里面向下的X有Q个,原来的效率是P*Q,现在均摊效率就是P+Q。太神了。据杜教说,效率均摊是N^1.5。GL。。。

【附:造数据程序】

#include<cstdio>
#include<ctime>
#include<cstdlib>
using namespace std;
struct arr{int x,y;}a[5000005];
int n,x,y,len,temp,j,i,cnt;
int main()
{
  freopen("p.in","w",stdout);
  srand((int)time(0));
  n=25000;int m=500,k=1000;
  printf("%d\n",n*4);
  for (i=1;i<=n;i++)
  {
    temp=rand()%10;
    if (!temp)
    {
      x=rand()%k-m;y=rand()%k-m;len=rand()%k+1;
      a[++cnt]=(arr){x,y};
      a[++cnt]=(arr){x+len,y+len};
      a[++cnt]=(arr){x,y+len};
      a[++cnt]=(arr){x+len,y};
    }
    else for (j=1;j<=4;j++) a[++cnt]=(arr){rand()%k-m,rand()%k-m};
  }
  for (i=1;i<=cnt;i++)
    printf("%d %d\n",a[i].x,a[i].y);
  return 0;
}

【AC代码】

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<set>
#define A 1000000
#define N 100005
using namespace std;
typedef set<int>::iterator It;It it,r,d,ok1,ok2;
long long ans;
set<int>P[N],Q[N];
int n,x,y1,y2,x1,x2,aa,i,m,rex[A*2+1],rey[A*2+1],px,py,cnt,cntt;
inline int Read()
{
  int x=0;char ch=getchar();bool positive=1;
  for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') positive=0;
  for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
  return positive?x:-x;
}
struct arr{int x,y;}a[N+1];
bool cmp1(arr a,arr b){return a.x<b.x||a.x==b.x&&a.y<b.y;}
bool cmp2(arr a,arr b){return a.y<b.y;}
int main()
{
  n=Read();
  for (i=1;i<=n;i++)
    a[i].x=Read(),a[i].y=Read();
  m=0;sort(a+1,a+n+1,cmp2);a[0].y=a[1].y-1;
  for (i=1;i<=n;i++) {m+=(a[i].y!=a[i-1].y);Q[m].insert(a[i].x);rey[a[i].y+A]=m;}
  m=0;sort(a+1,a+n+1,cmp1);a[0].x=a[1].x-1;
  for (i=1;i<=n;i++) {m+=(a[i].x!=a[i-1].x);P[m].insert(a[i].y);rex[a[i].x+A]=m;}
  for (i=1;i<=n;i++)
  {
    x1=a[i].x;y1=a[i].y;px=rex[x1+A];py=rey[y1+A];
    r=P[px].upper_bound(y1);d=Q[py].upper_bound(x1);
    for (;d!=Q[py].end()&&r!=P[px].end();)
    {
      int t1=*d,t2=*r;
      for (;(((*d)-x1)<((*r)-y1))&&(d!=Q[py].end());d++);
      for (;(((*r)-y1)<((*d)-x1))&&(r!=P[px].end());r++);
      if ((*d-x1)!=(*r-y1)||d==Q[py].end()||r==P[px].end()) continue;
      if (P[rex[*d+A]].find(*r)!=P[rex[*d+A]].end()) ans++;
      d++;r++;
    }
  }
  printf("%lld",ans);
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值