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; }