ZOJ 3521 Fairy Wars oj错误题目,计算几何,尺取法,排序二叉树,并查集 难度:2

时间:2023-03-10 07:03:28
ZOJ 3521 Fairy Wars oj错误题目,计算几何,尺取法,排序二叉树,并查集 难度:2

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3521

ATTENTION:如果用long long 减小误差,这道题只能用%lld读入

首先需要判断哪些点是相互挨着的,这样比直接维护哪些集合是冰冻住的简单

按照x为主,y为辅排序,在数组上尺取,当head与tail的x坐标相差大于l/2则把head向后移动直到x坐标满足条件,

那么对于head到tail,现在的问题就只剩下检测出哪些点之间y间距小于l/2,把它们都按照pair<y,id>加入set,那么离tail最近的两个点的y坐标就是离tail最近的,如果其中某个点满足间距小于l/2,那么就把tail和这个点放在一个并查集里,(其他的点如果满足条件,会和这两个点在一个并查集里)

最后统计一下哪些并查集是被冰冻住的,以及这些并查集中有多少点即可

#include <cstdio>
#include <cstring>
#include <set>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxn=5e4+3;
int n;
ll r,l;
P pnt[maxn],o;
int par[maxn];
bool in[maxn]; ll dis(P a,P b){return (a.first-b.first)*(a.first-b.first)+(a.second-b.second)*(a.second-b.second);} int fnd(int i){return(par[i]==i?i:(par[i]=fnd(par[i])));}
bool same(int a,int b){return fnd(a)==fnd(b);}
void unite(int a,int b){
if(!same(a,b)){
par[par[b]]=par[a];
}
} void solve(){
set<P> st;
int head=0;
for(int tail=0;tail<n;tail++){
while(2*abs(pnt[tail].first-pnt[head].first)>l){
st.erase(P(pnt[head].second,head));
head++;
}
set<P>::iterator it = st.insert(P(pnt[tail].second,tail)).first;
if(it!=st.begin()){
it--;
//printf("s:%lld %lld\n",it->first,pnt[tail].second);
if(2*abs(pnt[tail].second-it->first)<=l){
unite(tail,it->second);
}
it++;
} it++;
if(it!=st.end()){
if(2*abs(it->first-pnt[tail].second)<=l){
unite(tail,it->second);
}
}
}
} void output(){
int ans=0;
for(int i=0;i<n;i++){
if(dis(pnt[i],o)<=r*r){
if(!in[fnd(i)]){
in[par[i]]=true;
}
}
}
for(int i=0;i<n;i++){
if(in[fnd(i)]){
ans++;
}
} printf("%d\n",ans);
}
int main(){
while(scanf("%d%lld%lld",&n,&r,&l)==3){
memset(in,0,sizeof(in));
for(int i=0;i<n;i++){
scanf("%lld%lld",&(pnt[i].first),&(pnt[i].second));
par[i]=i;
}
scanf("%lld%lld",&(o.first),&(o.second)); sort(pnt,pnt+n); solve();
output();
}
return 0;
}