题解:P2794 Facer和教官
CDQ 分治的板子。
思路
先令 \(b_i\gets a_i-b_i\),则相似度就变成了 \(|b_i-b_j|+|a_i-a_j|\)。
考虑拆绝对值,对四个方向的点分别算贡献,设点 \(i\) 加入的时间为 \(t_i\),则点 \(j\) 能从左下方对 \(i\) 贡献的条件是:
\[\begin{cases}
a_j\leq a_i\\
b_j\leq b_i\\
t_j<t_i\\
\end{cases}
\]
它贡献的答案是 \(a_i+b_i-(a_j+b_j)\)。上面的贡献条件是一个三维偏序,所以可以直接套 CDQ 分治,先对时间轴分治,再对一维扫描线并维护另一维 \(a_j+b_j\) 的最大值。
其他三个方向也类似,跑四遍 CDQ 分治即可,时间复杂度 \(O((N+M)\log(N+M)\log V)\),其中 \(V\) 是 \(a\) 的值域。因为有四倍的常数,扫描线用线段树维护容易 TLE,建议将坐标加上 \(5\times 10^4+1\) 然后用树状数组维护。
代码
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define ll long long
const int N=1e5+5,P=1e9+7,sz=1e5+1,inf=1e9;
int n,m,k,ans[N];
struct opt
{
int t,x,y,id;
}a[2*N];
vector<opt> b;
namespace bit
{
int t[N];
void init()
{
for(int i=1;i<=sz;i++)
t[i]=-inf;
}
void update(int x,int y)
{
x+=5e4+1;
while(x<=sz)
{
t[x]=max(t[x],y);
x+=x&-x;
}
}
void clear(int x)
{
x+=5e4+1;
while(x<=sz)
{
t[x]=-inf;
x+=x&-x;
}
}
int getmax(int x)
{
x+=5e4+1;
int y=-inf;
while(x)
{
y=max(y,t[x]);
x-=x&-x;
}
return y;
}
}
void calc()
{
sort(b.begin(),b.end(),[](opt u,opt v){return u.y==v.y?u.id<v.id:u.y<v.y;});
for(auto p:b)
{
if(!p.id)
bit::update(p.x,p.x+p.y);
else
ans[p.id]=min(ans[p.id],p.x+p.y-bit::getmax(p.x));
}
for(auto p:b)
bit::clear(p.x);
}
void solve(int l,int r)
{
if(l==r)
return;
int mid=(l+r)>>1;
solve(l,mid);
solve(mid+1,r);
b.clear();
for(int i=l;i<=mid;i++)
if(!a[i].id)
b.push_back(a[i]);
for(int i=mid+1;i<=r;i++)
if(a[i].id)
b.push_back(a[i]);
calc();
for(auto &p:b)
p.x*=-1;
calc();
for(auto &p:b)
p.y*=-1;
calc();
for(auto &p:b)
p.x*=-1;
calc();
}
signed main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
a[i].t=i;
scanf("%d%d",&a[i].x,&a[i].y);
a[i].y=a[i].x-a[i].y;
}
for(int i=n+1;i<=n+m;i++)
{
a[i].t=i;
int op;
scanf("%d%d%d",&op,&a[i].x,&a[i].y);
a[i].y=a[i].x-a[i].y;
if(op==2)
a[i].id=++k;
}
memset(ans,0x3f,sizeof(ans));
bit::init();
solve(1,n+m);
for(int i=1;i<=k;i++)
printf("%d\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号