CQOI2016 bzoj4520 K远点对
2016-04-14 09:44
218 查看
题意
传送门已知平面内 N 个点的坐标,求欧氏距离下的第 K 远点对。
题解
题目越简单做起来越难恩题意即题解。
想到KK远点对做不起,只能做最远点对。于是果断最远点对,K=1K=1做出来。然后考虑怎么实现第K远。
想到可以每次求凸包然后旋转卡壳求最远点对然后删除。问题又出现了:删除的点仍有可能对K远点对作出贡献。但是,第KK远一定是每个点前KK远合并之后的第K远。所以,我们将删除的点与所有点求距离,存下来,然后重复这一操作。这样的时间复杂度大概是O(nKlogK)O\left( nK \log K \right),这让我们有些不爽,所以要尽可能优化过程。想到不能再极角排序了,所以我们以1为原点,每次删除编号较大的点,就可以保证不用再次极角排序。
每次删除后得到的距离可以用可并堆维护,只保留KK个点。算是可并堆经典应用吧。这样很有意思。
代码
#include<cstdio> #include<algorithm> #include<vector> using namespace std; typedef long long LL; const int NUM=100005; const LL INF=(1LL<<62); const int KSIZE=105; inline LL Sqr(LL x){return x*x;} inline int min(int a,int b){return a<b?a:b;} inline LL Abs(LL a){return a<0?-a:a;} inline void Swap(int &a,int &b){int t=a;a=b;b=t;} struct Point{ LL x,y; }P[NUM]; inline LL Cross(Point a,Point b,Point o) { return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);} inline LL SqDis(Point a,Point b) { return Sqr(a.x-b.x)+Sqr(a.y-b.y);} inline void SwapPoint(Point &a,Point &b) { Point t=a;a=b;b=t;} int n,K; vector<int>rec; void Read() { int i; scanf("%d%d",&n,&K); for(i=1;i<=n;i++) scanf("%lld%lld",&P[i].x,&P[i].y); } bool cmp(Point a,Point b) { LL tmp=Cross(a,b,P[1]); if(tmp>0)return 1; if(tmp<0)return 0; return SqDis(a,P[1])<SqDis(b,P[1]); } const int TOT=10*KSIZE; int tot,L[TOT],R[TOT],Size[TOT];LL val[TOT]; inline int Newnode(LL v) { int x; if(rec.size()){x=rec.back();rec.pop_back();} else x=++tot; val[x]=v;L[x]=R[x]=0;Size[x]=1;return x; } int Merge(int x,int y) { if(!x||!y)return x+y; if(val[x]>val[y])Swap(x,y); R[x]=Merge(R[x],y); Swap(L[x],R[x]); Size[x]=Size[L[x]]+1+Size[R[x]]; return x; } inline int Del(int x) { int t=Merge(L[x],R[x]); rec.push_back(x); return t; } int stk[NUM],top;bool vst[NUM]; void Solve() { int i,k;LL Minx=INF,Miny=INF; int p,Rt=0,now,s,t; LL tmp,Max; rec.clear(); for(i=1;i<=n;i++) if(P[i].y<Miny||(P[i].y==Miny&&P[i].x<Minx)) { Miny=P[i].y;Minx=P[i].x;k=i;} SwapPoint(P[1],P[k]); sort(P+2,P+1+n,cmp); int Up=min(K,n-1); for(i=1;i<=n;i++)vst[i]=0; for(k=1;k<=Up;k++) { stk[top=1]=1; for(i=2;i<=n;i++) if(!vst[i]) { while(top>=2&&Cross(P[i],P[stk[top-1]],P[stk[top]])<=0) top--; stk[++top]=i; } p=2;stk[top+1]=stk[1];Max=0; for(i=1;i<=top;i++) { while(Abs(Cross(P[stk[i]],P[stk[i+1]],P[stk[p]])) <Abs(Cross(P[stk[i]],P[stk[i+1]],P[stk[p+1]]))) { p++;if(p==top+1)p=1;} tmp=SqDis(P[stk[i]],P[stk[p]]); if(tmp>Max){Max=tmp;s=stk[i];t=stk[p];} tmp=SqDis(P[stk[i+1]],P[stk[p]]); if(tmp>Max){Max=tmp;s=stk[i+1];t=stk[p];} } now=0;if(s>t)Swap(s,t); vst[t]=1; for(i=1;i<=n;i++) if(!vst[i]) { now=Merge(now,Newnode(SqDis(P[t],P[i]))); if(Size[now]>K)now=Del(now); } Rt=Merge(Rt,now); while(Size[Rt]>K)Rt=Del(Rt); } printf("%lld\n",val[Rt]); } int main(){ Read(); Solve(); return 0; }
相关文章推荐
- 【iOS开发】iOS极光推送 点击推送消息跳转页面
- 每天laravel-20160714|AppNamespaceDetectorT
- C#中is与as的区别分析
- 三角函数
- 从电脑上输入一段字符串
- IOS开发中如何实现自动检测更新APP
- leetcode 15:3 Sum (C#语言版)
- 多线程理解之线程自述(我是一个线程)
- 每天laravel-20160713|ScheduleRunCommand
- 每天laravel-20160714|AppNamespaceDetectorT
- iOS 线程之GCD的高级使用方法
- IOS开发之—— 上传头像的使用
- python线程的一些疑问
- matlabR2015b安装libsvm
- 如何将应用程序exe注册成服务,直接从后台运行,如何删除windows服务
- Jquery1.9.1关于checked的哪点破事
- matlab中clear,clc,clf,hold作用
- Nginx+Memcached+Tomcat集群配置实践
- Java reflect--动态执行Method-代码
- Android Pingpong漏洞解析