关于支配对
这种方法一般用在数据结构中找两个点使得代价最小的这类问题中。本质就是会对答案造成贡献的点对数(可能成为最优解)是 \(O(n \log n)\) 级别的。从而可以暴力找出这些点对转化为二维数点的问题。
例1
有若干个点,区间询问,从中选出两个点,使得这两个点的曼哈顿距离/切比雪夫距离/欧几里得距离最小。
对于曼哈顿距离这类的可以离线后二维数点,但并不好处理区间询问。所以可以参考平面最近点对这道题的思路。这道题是通过在一个 \(d \times d\) 的正方形中至多存在 \(4\) 个距离 \(>=d\) 的点的结论从而可以暴力枚举所有可能的点对,这就说明其实真正可能对答案造成贡献的只有 \(O(n \log n)\) 级别。但是这道题需要先分治求出左右两边的答案再进行合并。这不太好处理区间询问。
所以我们考虑另一种思路,我们从小到大依次枚举 \(2^i\),判断答案是否 \(\leq 2^i\) 。枚举到 \(i\) 时就说明不存在距离 \(\leq 2^{i-1}\) 的点,那么对于任意一个 $2^i \times 2^i $ 的正方形中最多只会存在 \(O(1)\) 个点 (具体来说,曼哈顿距离时最多 \(4\) 个点,欧几里得距离时最多 \(9\) 个点,只是一个上界)。所以每一个正方形中的每一个点都只有 \(O(1)\) 的点和它构成支配对 (可能成为答案的点对)。所以就只有 \(O(n \log V)\) 的点对数。
具体实现过程就是把坐标系划分成若干个 \(2^k \times 2^k\) 的正方形,对于一个点 \(i\),我们考虑所有 \(i\) 前面的 \(j\)。那么我们扫描线是从小到大枚举右端点 \(i\)。加入所有的点对 \(j,i\) (\(j<i\))。查询就是所有左端点 \(\geq l\) 的点对的最小值。所以我们需要找到所有可能和 \(i\) 成为最优解的 \(j\)(\(j<i\))并且 \(i,j\) 的距离在 \((2^{k-1},2^k]\) 之间,我们已经把平面划分为若干个 \(2^k \times 2^k\) 的正方形,找到 \(i\) 所在的正方形,发现 \(j\) 只可能在 \(i\) 所在的正方形的周围 \(9\) 个正方形内 (不然就超过 \(2^k\) 了)。同时,我们取每一个正方形内编号\(\leq i\) 中标号最大的 \(4\) 个点。这就说明其他的点和 \(i\) 不可能成为最优解。
证明一下,假如对 \(\leq i\) 的标号排序之后的序列是 \(p_1 \space p_2 \space p_3 \space p_4 \space p_5\) 那么如果最优解是 \((p_1,i)\) 这个点对,那么这个询问的区间一定包含了 \([p_1,i]\),那么一定包含了 \(p_1 \space p_2 \space p_3 \space p_4 \space p_5\) 这 \(5\) 个点,而这 \(5\) 个点在一个 \(2^k \times 2^k\) 的正方形中,那么最终的答案一定 \(\leq 2^{k-1}\),所以不可能成为最优解。
所以我们就找到了所有的支配点对,而数量是 \(O(n \log V)\) 级别的,不过可能有 \(20\) 左右的常数。最终复杂度为 \(O(n \log V \log n)\)。注意点对要去重,可以优化很多。
关于预处理所有支配点对的话可以用 \(map\) 维护每个正方形内的所有点。可以参考一下我的实现:
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
const int N=3e4+5,I=1e9,B=3e5+5;
int n,x[N],y[N],q,l,r,ans[B],id[N],t[N];
vector<int> f[N];
map<ll,pair<int,int>> mp;
vector<pair<int,int>> s,g[N];
ll F(int a,int b) {return (ll)a<<30|b;}
void add(int u,int v) {f[v].push_back(u);} //加入支配点对
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d%d",&l,&r);
g[r].push_back({l,i});
}
for(int k=0;k<=27;k++)
{
mp.clear();
for(int i=1;i<=n;i++) id[i]=i;
sort(id+1,id+n+1,[&](int i,int j){return make_pair(x[i]>>k,y[i]>>k)<make_pair(x[j]>>k,y[j]>>k);});
for(int l=1,r;l<=n;l=r+1)
{
r=l;
while(r<n&&(x[id[l]]>>k)==(x[id[r+1]]>>k)&&(y[id[l]]>>k)==(y[id[r+1]]>>k)) r++;
mp[F(x[id[l]]>>k,y[id[l]]>>k)]={l,r};
sort(id+l,id+r+1);
for(int i=l;i<=r;i++)
for(int j=i+1;j<=min(r,i+5);j++)
add(id[i],id[j]);
}
for(int l=1,r;l<=n;l=r+1)
{
r=l;
while(r<n&&(x[id[l]]>>k)==(x[id[r+1]]>>k)&&(y[id[l]]>>k)==(y[id[r+1]]>>k)) r++;
int a=(x[id[l]]>>k),b=(y[id[l]]>>k);
for(int dx:{-1,0,1}) for(int dy:{-1,0,1})
{
if(a+dx<0||b+dy<0||mp.find(F(a+dx,b+dy))==mp.end()||(dx==0&&dy==0)) continue;
auto u=mp[F(a+dx,b+dy)];
for(int i=l,k=u.fi-1;i<=r;i++)
{
while(k<u.se&&id[k+1]<id[i]) k++;
for(int j=max(u.fi,k-4);j<=k;j++) add(id[j],id[i]);
}
}
}
}
return 0;
}

浙公网安备 33010602011771号