【DS】P9062 [Ynoi2002] Adaptive Hsearch&Lsearch(区间最近点对)

发布时间 2023-07-10 20:14:10作者: CharlieVinnie

Problem Link

给定平面上 \(n\) 个点,\(Q\) 次询问编号在 \([l,r]\) 内的点的最近点对。\(n,Q\le 2.5\times 10^5\)


技巧:平面网格化

乱搞都是错的。看见欧几里德距离,想到平面网格化。

考虑一个平面最近点对的网格化做法:随机点的顺序,按顺序依次考虑,考虑到第 \(i\) 个点时,设当前最近点对距离为 \(d\),则把平面划分为 \(d\times d\) 的网格,直接枚举 \(i\) 所在周围 \(9\) 个格子中 \(O(1)\) 个点即可。时间复杂度 \(\sum i\times O(1/i)=O(n)\),但这不是重点。重点在于网格化。

现在 \(d\) 不能确定,于是令 \(d\) 为一个定值,然后将平面划分 \(\log_d V\) 次,每次划分为 \(d^k\times d^k\) 的网格。同理每个网格中需要保留的点距离均大于 \(d^{k-1}\),所以总点数应该是 \(O(d)\) 的。几倍常数不知道。

扫右端点 \(r\),对每个点 \(i\) 维护在 \(j>i\)\(j\)\(\mathrm{dis}(i,j)\) 的最小值,那询问就查 \(l\) 后缀最小值即可。扫到点 \(r\) 的时候,对每一层,暴力更新周围 \(3\times 3\) 的网格,更新权值,同时把没用的点扬掉。

一共会进行 \(O(n\log_d V)\) 次单点修改,但每次修改常数巨大巨大。有 \(O(Q)\) 次查询。拿个分块数组维护即可做到 \(O(n\log_d V+Q\sqrt{n})\)

\(d\) 开成 \(4\),位运算,好写,又快。\(d=2\) 时间空间都开不下。

点击查看代码
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#define For(i,a,b) for(int i=a;i<=b;i++)
#define Rev(i,a,b) for(int i=a;i>=b;i--)
#define Fin(file) freopen(file,"r",stdin);
#define Fout(file) freopen(file,"w",stdout);
using namespace std;
const int N=2.5e5+5,M=1.5e7,V=1e8; using ll = long long;
class STree{;
	int n,B; ll a[N],b[N];
public:	
	void init(int nn) { n=nn; B=sqrt(n)+1; For(i,0,n) a[i]=b[i]=1e18; }
	void poke(int x,ll y) { a[x]=min(a[x],y); b[x/B]=min(b[x/B],y); }
	ll peek(int x) { return min(*min_element(a+x,a+min(n,x/B*B+B-1)+1),*min_element(b+x/B+1,b+n/B+1)); }
	//~ ll peek(int x) { ll res=1e18; For(i,x,min(n,x/B*B+B-1)) res=min(res,a[i]);; For(i,x/B+1,n/B) res=min(res,b[i]);; return res; }
}T;
int n,Q,X[N],Y[N],tot; vector<pair<int,int>> qry[N]; vector<int> lis[M]; ll ans[N];
__gnu_pbds::gp_hash_table<ll,int> mp[30];
//~ unordered_map<ll,int> mp[30];
ll dist(int i,int j) { return 1ll*(X[i]-X[j])*(X[i]-X[j])+1ll*(Y[i]-Y[j])*(Y[i]-Y[j]); }
ll ID(int x,int y) { return (ll(x)*10*V)+y; }
int Cnt=0;
void update(int u,ll id,int i,bool flg){
	if(!flg&&mp[i].find(id)==mp[i].end()) return;
	if(mp[i].find(id)==mp[i].end()) mp[i][id]=++tot,assert(tot<M);
	int o=mp[i][id]; vector<int> tmp; if(flg) tmp.push_back(u);
	for(int v:lis[o]){
		Cnt++;
		ll d=dist(u,v); T.poke(v,d); if(d>(i==0?0:1ll<<(i*4-4))) tmp.push_back(v);
	}
	swap(lis[o],tmp);
}
int main(){
	//~ Fin("hh.in"); Fout("hh.out");
	ios::sync_with_stdio(0); cin.tie(0);
	cin>>n>>Q; T.init(n); For(i,1,n) cin>>X[i]>>Y[i];
	For(i,1,Q) { int l,r; cin>>l>>r; qry[r].emplace_back(l,i); ans[i]=1e18; }
	For(r,1,n){
		for(int i=0;(1<<(i*2))<=V;i++){
			int x=X[r]>>(i*2),y=Y[r]>>(i*2);
			For(xx,x-1,x+1) For(yy,y-1,y+1){
				if(xx<0||yy<0||xx>(V>>(i*2))||yy>(V>>(i*2))) continue;
				update(r,ID(xx,yy),i,xx==x&&yy==y);
			}
		}
		for(auto [l,id]:qry[r]) ans[id]=min(ans[id],T.peek(l));
	}
	For(i,1,Q) cout<<ans[i]<<'\n';
	return 0;
}