非分治算法P7883
看了看题解区除了 \(\tt KD-Tree\) 和随机化乱搞,就只有分治做法
这里记录一下 \(\tt OI-Wiki\) 上提到的非分治算法
其实是我只会这个
有一种很朴素的统计思想,就是对于序列里每个点求出它和之前的点的答案,并统一为最终答案
但是硬要这么做也是 \(O(n^2)\) 的
所以就考虑怎么优化
有一个朴素的限制:
设处理到点 \(P_i\) 时的答案为 \(ans_i\)
对于每个点 \(P_i(x_i,y_i)\) ,那些 \(\lvert y_j-y_i \rvert \gt ans_i\) 或者 \(\lvert x_j-x_i \rvert \gt ans_i\) 的点 \(P_j\) 肯定不能和它一起更新答案
要执行这个限制很简单,像求凸包一样按坐标排序
然后引入一个配合它可以直接把复杂度搞下去的东西
\[\sf\large Multiset
\]
然后就重载()进行排序
各种操作复杂度是祖传的 \(log\,n\)
code:(不是二次加强版的,是加强版的)
#include <iostream>
#include <cmath>
#include <algorithm>
#include <set>
#include <iomanip>
using namespace std;
#define db double
#define ri register int
#define MAXN 200001
struct node{
db x,y;
node(db x=0,db y=0) : x(x),y(y){ }
}a[MAXN];
inline bool cmp1(node a,node b){ return a.x!=b.x?a.x<b.x:a.y<b.y; }
struct cmp2{
inline bool operator ()(const node &a,const node &b) const { return a.y<b.y; }
};
multiset<node,cmp2> s;
db ans=2e10; int n;
inline db sqr(db x){ return x*x; }
inline db dist(node a,node b){ return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y)); }
inline void syncwithme(){
ios::sync_with_stdio(false);
cin.tie(NULL),cout.tie(NULL);
}
int main()
{
syncwithme();
cin>>n; db x,y;
for(ri i=1;i<=n;i++)
cin>>x>>y,a[i]=node(x,y);
sort(a+1,a+n+1,cmp1); //先按x第一关键字,y第二关键字排序
ri l=1;
for(ri i=1;i<=n;i++){
while((l^i)&&(a[i].x-a[l].x>ans)) //test
//直观上对于横坐标之差就已经大于ans的直接删除,不会影响后面(后面的点因为按x排过序,横向距离只会更远)
s.erase(s.find(a[l])),++l;
for(auto it=s.lower_bound(node(a[i].x,a[i].y-ans)); //y1-ans是因为能更新答案的范围是y1-y2∈(-ans,ans),先确定了起始点
it!=s.end()&&it->y-a[i].y<ans;++it){
ans=min(ans,dist(a[i],*it)); //对于这些点一个一个判断是否更新答案
} s.insert(a[i]); //再插入点i,用于后面点的更新
} cout<<fixed<<setprecision(4)<<ans;
return 0;
}
//正确性:只删除了已经不可能影响答案的点,并且没有超出更新答案的范围
//复杂度:排序O(nlogn) + 遍历O(n)*(插入O(logn)+删除O(logn)) =O(nlogn)
//(每个点只插入了一次,删除了一次)
update:
好像和分治的思路有点像?
bzd bzd(

浙公网安备 33010602011771号