F. Closest Pair
F. Closest Pair
题目大意
- 给定 \(n(2 \le n \le 3\times 10^5)\) 个二元组 \((x_i,w_i)\),其中 \(|x_i|\le 10^9\),\(1\le w_i \le 10^9\)。
- 输入中二元组按照 \(x_i\) 严格递增排序给出。
- 给出 \(q(1\le q \le 3\times 10^5)\) 次询问,每次询问给出 \(l,r(1\le l<r \le n)\),你需要输出:
\[\min_{l\le i<j\le r} |x_i-x_j| \cdot (w_i+w_j)
\]
分析
这里,用到了两个比较有趣的算法。
我们先来一步步分析。
我们来看这个式子,\(|x_i-x_j|*(w_i+w_j)\),可以发现如果两个位置i,j
之间,有比\(w_i,w_j\)还小的数的话,那么这一对点一定不会成为我们的目标答案。这点是很好推导出来了,因此我们的合法答案一定是,\(max(w_i,w_j)>min_{k=i+1}^{j-1}w_k\)的,即两点之间的权值一定比两者权值大。我们可以发现这就是类似于山峰的形式。因此我们不难想到,可以利用单调栈去计算得出所有合法的答案。
我们维护一个递增的单调栈,当将一个点入栈时,我们可以发现,所有的被弹出的点,都可以与入栈的该点成为合法点对,最后剩余的栈顶也可以与该点构成合法点对。这个算法很妙。这样操作完,我们发现,我们的点对数量也就是\(O(n)\)的。
然后,我们的问题就变为了,在所有的合法点对中,找到最小的了。这个问题就类似于之前做过的一道题目,CF522D Closest Equals,这题是要求所有相同的点构成的点对中距离最小的点对。
这个问题还是蛮经典的了,我们考虑使用线段树离线去做。
我们将所有的点对按右端点从大到小排序,或者不排序,直接开一个数组去存储,离线怎么做都行啦。这里我们采用第二种方法,我们反向枚举,枚举到一个点时,将该点是左端点的所有合法点对的权值,在右端点修改。然后将以该点为左端点的询问全部进行了。就结束啦。
AC_code
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
using ll = long long;
const int N = 3e5 + 10;
const ll inf = 8e18;
struct Node
{
int l,r;
ll mx,mi;
}tr[N<<2];
ll n,q,w[N],len;
void pushup(int u)
{
tr[u].mi = min(tr[u<<1].mi,tr[u<<1|1].mi);
tr[u].mx = max(tr[u<<1].mx,tr[u<<1|1].mx);
}
void build(int u,int l,int r)
{
tr[u] = {l,r,0,inf};
if(l==r) return ;
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void modify(int u,int x,ll v)
{
if(tr[u].l==tr[u].r)
{
tr[u].mi = min(tr[u].mi,v);
tr[u].mx = max(tr[u].mx,v);
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x<=mid) modify(u<<1,x,v);
else modify(u<<1|1,x,v);
pushup(u);
}
ll querymin(int u,int l,int r)
{
if(tr[u].l>r||tr[u].r<l) return inf;
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].mi;
return min(querymin(u<<1,l,r),querymin(u<<1|1,l,r));
}
ll querymax(int u,int l,int r)
{
if(tr[u].l>r||tr[u].r<l) return 0;
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].mx;
return max(querymax(u<<1,l,r),querymax(u<<1|1,l,r));
}
struct node
{
int r;
ll v;
};
vector<node> Seg[N],Que[N];
ll ans[N],x[N];
int stk[N],tt;
int main()
{
ios;
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>x[i]>>w[i];
for(int i=1;i<=n;i++)
{
while(tt&&w[stk[tt]]>=w[i])
{
int t = stk[tt];
Seg[t].push_back({i,(w[i] + w[t])*(x[i] - x[t])});
tt--;
}
if(tt)
{
int t = stk[tt];
Seg[t].push_back({i,(w[i] + w[t])*(x[i] - x[t])});
}
stk[++tt] = i;
}
for(int i=1;i<=q;i++)
{
int l,r;cin>>l>>r;
Que[l].push_back({r,i});
}
build(1,1,n);
modify(1,n,inf);
for(int i=n-1;i;i--)
{
for(auto st:Seg[i])
{
int r = st.r;
ll v = st.v;
modify(1,r,v);
}
for(auto st:Que[i])
{
int l = i,r = st.r,id = st.v;
ans[id] = querymin(1,l,r);
}
}
for(int i=1;i<=q;i++) cout<<ans[i]<<'\n';
return 0;
}