• 【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_axa<xb<xc w b < w c w_bwb<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 cc<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_ii,\ W_{r_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(a,b)wa<wb,满足 a > l b a>l_b a>lb

    按照上面得到的性质,如果 w a < w b w_awa<wb a < b aa<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
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
  • 相关阅读:
    windows内核编程(2021年出版)笔记
    ELK日志系统
    计算机网络 | 第一章 认识计算机网络 | 王道考研笔记自用
    SQL注入漏洞(原理篇)
    linux配置免密登陆
    【Linux】环境变量
    棒球元宇宙的未来·棒球9号位
    C语言中的函数openlog
    Python yield 使用浅析
    Hadoop3教程(三十五):(生产调优篇)HDFS小文件优化与MR集群简单压测
  • 原文地址:https://blog.csdn.net/ez_gsn/article/details/125885260