【CF1635F】 Closest Pair 题解

CF 传送门:CF1635F

找性质规律 + 单调栈 + 线段树

Solution

1

考场上想出了把那些点对扔进单调队列然后乱搞的做法。发现对于一个询问区间,如果想要直接都扔进单调队列然后从中找到带权距离最小值复杂度仍然很高。

换句话说,我们无法直接得到区间最优解。那不妨来试着求解单点最优解,然后在询问区间内合并答案得到最优。

2

一般这种情况,需要我们讨论 3 个或以上个节点寻找性质规律。先考虑全局。

不妨假设有三个节点 a ,   b ,   c a,\ b,\ c a, b, c,满足 x a < x b < x c x_a<x_b<x_c xa<xb<xc w b < w c w_b<w_c wb<wc。不考虑 w a w_a wa 的数量关系是因为若这个性质需要 w w w 也满足单调( x x x 本身满足单调),那么限制条件过多,不好做。

我们由此可以发现,三点不论怎样组合, d ( a , c ) d(a,c) d(a,c) 一定不是最优的—— d ( a , b ) ,   d ( b , c ) d(a,b),\ d(b,c) d(a,b), d(b,c) 显然都比他优。又因为我们要寻找单点最优解,故对于节点 c c c 而言,节点 b b b 之前的节点对它都没有意义(换句话说,对全局不会有贡献)。 这个性质对 c < b < a c<b<a c<b<a 的情况同样成立。

综述,对于每个节点 i i i,我们都可以找到两个相应节点 l i ,   r i l_i,\ r_i li, ri,满足 l i < i ,   W l i < W i ,   r i > i ,   W r i < W i l_i<i,\ W_{l_i}<W_i,\ r_i>i,\ W_{r_i}<W_i li<i, Wli<Wi, ri>i, Wri<Wi。而有且仅有这 2 n 2n 2n 个点对可以对全局结果产生贡献(因为它们相对其他点对都更优)。

3

回过头看区间上如何处理。

自然会有疑问:对于一个单点 i i i,如果其 l i l_i li r i r_i ri 都在区间询问范围之外,即我们考虑不到,但是 i i i 本身却可以和区间内其他节点构成本区间的最优解呢?

为了解决这个问题,不妨构造出一个和问题所述一样的情景:对于询问区间 [ L , R ] [L,R] [L,R],有带权距离最小的点对 ( a , b ) ∣ w a < w b (a,b)|w_a<w_b (a,b)wa<wb,满足 a > l b a>l_b a>lb

按照上面得到的性质,如果 w a < w b w_a<w_b wa<wb a < b a<b a<b,那么显然 l b = a l_b=a lb=a,与假设矛盾。而 w b > w a w_b>w_a wb>wa 的情况同理。

综上,我们得到结论:区间内的带权距离最小的点对一定属于那 2 n 2n 2n 个点对之中。

4

l i l_i li r i r_i ri 可以通过单点栈得到。

直觉经验告诉我们查找区间最优点对可以使用线段树实现。具体地,将每个 d ( l i , i ) d(l_i,i) d(li,i) 存在点 l i l_i li 上, r i r_i ri 同理,再将询问离线下来,按 R R R 升序依次查询处理即可。

Code

#include<bits/stdc++.h>
using namespace std;
 
#define int long long
#define per(i, a, b) for(register int i = a; i >= b; --i)
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
const int maxn = 3e5 + 5;
int n, q;
int x[maxn], w[maxn], st[maxn], top;
struct node{int l, id;};
vector<node> que[maxn];
int ans[maxn];
vector<int> p[maxn];
int fr[maxn], bc[maxn], l, r;
 
struct segment_tree{
    #define ls(x) (x<<1)
    #define rs(x) (x<<1|1)
    int mn[maxn << 2];
    inline void updt(int i, int l, int r, int pos, int k){
        if(l == r){
            mn[i] = min(mn[i], k); return;
        }
        int mid = l + r >> 1;
        if(pos <= mid) updt(ls(i), l, mid, pos, k); else updt(rs(i), mid + 1, r, pos, k);
        mn[i] = min(mn[ls(i)], mn[rs(i)]);
    }
    inline int qry(int i, int l, int r, int L, int R){
        if(l >= L and r <= R) return mn[i];
        int mid = l + r >> 1, res = 5e18;
        if(L <= mid) res = min(res, qry(ls(i), l, mid, L, R));
        if(R > mid) res = min(res, qry(rs(i), mid + 1, r, L, R));
        return res;
    }
}T;
 
signed main(){
    scanf("%lld%lld", &n, &q);
    rep(i, 1, n << 2) T.mn[i] = 5e18;
    rep(i, 1, n) scanf("%lld%lld", &x[i], &w[i]);
    rep(i, 1, n){
        while(top and w[st[top]] > w[i]) top -= 1;
        fr[i] = st[top], st[++top] = i;
    } top = 0;
    per(i, n, 1){
        while(top and w[st[top]] > w[i]) top -= 1;
        bc[i] = st[top], st[++top] = i;
    }
    rep(i, 1, n){
        if(fr[i]) p[i].push_back(fr[i]);
        if(bc[i]) p[bc[i]].push_back(i); 
    }
    rep(i, 1, q) 
        scanf("%lld%lld", &l, &r), ans[i] = 5e18, que[r].push_back({l, i});
    rep(i, 1, n){
        for(auto j : p[i]) T.updt(1, 1, n, j, 1ll * abs(x[i] - x[j]) * (w[i] + w[j]));
        for(auto j : que[i]) ans[j.id] = T.qry(1, 1, n, j.l, i);
    }
    rep(i, 1, q) printf("%lld\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值