分析:
模拟实现题。。。把坐标轴转一下然后暴力求就行了。
转了一下坐标轴,问题就变成以p为中心,与新的坐标轴平行的,边长为2*d的正方形上的点能够与p相连。
方便起见,把答案分成两部分求
然后可以分别考虑这两部分,分别扫描这两部分即可。
只不过有个技巧,如果使用并查集储存能到达哪些点,那么每次连的边应该从与上个点最后一个连的点开始即可。(没必要重复连边,因为那部分已经连上同一个点,所以只需要连一条就能把所有点连在一起)。这样一来,连边的次数均摊下来就是2*N次了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<vector>
#include<set>
#define SF scanf
#define PF printf
#define MAXN 200010
using namespace std;
typedef long long ll;
int n,st;
ll d;
int fa[MAXN];
ll siz[MAXN];
ll cnt[MAXN];
int get_fa(int x){
if(fa[x]==0)
return x;
fa[x]=get_fa(fa[x]);
return fa[x];
}
vector<pair<ll,int> >mp;
struct node{
ll x,y;
int id;
bool operator < (const node &a) const {
if(x!=a.x)
return x<a.x;
return y<a.y;
}
}p[MAXN];
ll dist(int x,int y){
return abs(p[x].x-p[y].x)+abs(p[x].y-p[y].y);
}
void merge(int x,int y){
x=get_fa(x);
y=get_fa(y);
if(x!=y)
fa[x]=y;
}
void solve(ll now){
mp.clear();
int las=1;
vector<pair<ll,int> >::iterator lasx;
bool flag=0;
for(int i=1;i<=n;i++){
if(p[i].x!=p[i-1].x){
mp.clear();
flag=1;
}
while(p[i].x-p[las].x>d&&las<=n)
las++;
while(p[i].x-p[las].x==d&&las<=n){
mp.push_back(make_pair(p[las].y,p[las].id));
las++;
}
vector<pair<ll,int> >::iterator it=lower_bound(mp.begin(),mp.end(),make_pair(p[i].y-d+now,0));
if(flag){
flag=0;
lasx=mp.begin();
}
lasx=max(lasx,it);
if(it==mp.end()||it->first >p[i].y+d-now)
continue;
for(;lasx!=mp.end()&&lasx->first <= p[i].y+d-now;lasx++){
int x=lasx->second;
int y=p[i].id;
if(x>y)
swap(x,y);
merge(x,y);
}
cnt[p[i].id]+=(lasx-it);
if(lasx!=it)
lasx--;
}
}
int a,b;
int main(){
SF("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++){
SF("%d%d",&p[i].x,&p[i].y);
p[i].id=i;
}
d=dist(a,b);
for(int i=1;i<=n;i++){
ll x=p[i].x;
ll y=p[i].y;
p[i].x=x-y;
p[i].y=x+y;
}
sort(p+1,p+1+n);
solve(0);
for(int i=1;i<=n;i++)
swap(p[i].x,p[i].y);
sort(p+1,p+1+n);
solve(1);
for(int i=1;i<=n;i++)
siz[get_fa(i)]+=cnt[i];
PF("%lld",siz[get_fa(a)]);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)