CF1635F Closest Pair 题解

CF1635F Closest Pair

题目大意:

给定一个序列,每个点 \(i\) 有两个权值分别为 \(a_i\) , \(w_i\)

满足 \(a\) 单调递增。

定义一个点对的价值为 \(val = \mid a_i - a_j \mid \times (w_i + w_j)\)

\(q\) 次询问,求一个区间内最小的点对的价值。

题解:

首先这是个结论题(贪心题)。

先放结论:

定义 \(L_i\) 表示最大的 \(j\) 满足 \(j < i\)\(w_j \le w_i\),定义 \(R_i\) 表示最小的 \(j\) 满足 \(j > i\)\(w_j \le w_i\)

最终答案一定在 \((L_i, i)\)\((i, R_i)\)\(2n\) 个点对里。

证明:

设三个点 \(i, j, k\) \((i < j < k)\)

其中 \(j\)\(L_k\),即 \(w_j \le w_k\)

现在证明当询问区间包含 \(i, j, k\) 时, \((i, k)\) 一定不会是最优决策。

分情况讨论:

注意前提条件是:

\(a_i < a_j < a_k\)\(w_j \le w_k\)

1. \(w_i > w_j\)

那么 \(\mid a_i - a_k \mid \ > \ \mid a_j - a_k \mid\)\((w_i + w_k) > (w_j + w_k)\)

相比于 \((j, k)\)\((i, k)\) 一定不优。

2. \(w_i \le w_j\)

此时有 \(a_i < a_j < a_k\)\(w_i \le w_j \le w_k\)

那么 $ \mid a_i - a_k \mid \ > \ \mid a_i - a_j \mid $ 且 \((w_i + w_k) > (w_i + w_j)\)

那么点对 \((i, j)\) 一定是比点对 \((i, k)\) 更优。

同理 \(R_k\) 的证明也是这样。

(接下来变量名要换一下啦,设我们选定的位置为 \(i\)

那么此时,对于任意的 \(i\) ,可选的决策点就在 \([L_i, R_i]\) 当中。

(当然要把自己除去啦~~)

然后我们接着证明:

只选择点对 \((L_i, i)\)\((i, R_i)\) 一定不会漏掉最优决策。

其实仔细思考一下就知道啦。

对于可选集合 \([L_{i_1}, L_{i_2}, L_{i_3} \cdot \cdot \cdot i)\) 中,可能成为最优决策的决策点(们)一定满足以下条件:

\(w_{L_{i_1}} \le w_i < w_{L_{i_2}} < w_{L_{i_3}} < \cdot \cdot \cdot < w_{L_{i_k}}\)

(用 \(j\) 代替 \(w_{L_{i_2}}\)

假设点对 \((j, i)\) 成为了最优答案。

注意到此时满足 \(i > j\)\(w_i < w_j\)

这种情况实际上是点对 \((j, R_j)\)

即如果只选这样的点对 \((L_i, i)\)\((i, R_i)\) 足以包含所有最优决策。

有了这个结论剩下的就很简单了,先单调栈求 \(L_i, R_i\) ,再把询问离线下来扫一遍即可。

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

char buf[1 << 23], *p1 = buf, *p2 = buf;
#define Get() ((p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, 1 << 23, stdin), p1 == p2) ? EOF : *p1++)

int read()
{
    int x = 0; bool f = false; char c = Get();
    while(c != EOF && c < '0') f |= (c == '-'), c = Get();
    while(c != EOF && c >= '0') x = (x << 1) + (x << 3) + (c & 15), c = Get();
    return f ? -x : x;
}

const int N = 3e5 + 5;
int n, m;
ll x[N], w[N];
int L[N], R[N];
int sta[N], top;
vector< pair<int, int> > wen[N];
vector<int> pos[N];
ll ans[N];

struct BIT
{
    ll c[N];
    BIT(){ memset(c, 0x7f, sizeof(c)); }
    void change(int k, ll val){ while(k) c[k] = min(c[k], val), k ^= (k & -k); }
    ll query(int k){ ll ans = 0x7fffffffffffffff; while(k <= n) ans = min(ans, c[k]), k += (k & -k); return ans; }
}T;

int main()
{
    n = read(), m = read();
    for(int i = 1; i <= n; ++i) x[i] = read(), w[i] = read();
    for(int i = 1; i <= m; ++i)
    {
        int l = read(), r = read();
        wen[r].emplace_back(pair<int, int>(l, i));
    }

    for(int i = 1; i <= n; ++i)
    {
        while(top && w[sta[top]] > w[i]) --top;
        if(top) L[i] = sta[top];
        sta[++top] = i;
    }

    top = 0;

    for(int i = n; i >= 1; --i)
    {
        while(top && w[sta[top]] > w[i]) --top;
        if(top) R[i] = sta[top];
        sta[++top] = i;
    }

    for(int i = 1; i <= n; ++i)
    {
        if(L[i]) pos[i].emplace_back(L[i]);
        if(R[i]) pos[R[i]].emplace_back(i);
    }

    for(int i = 1; i <= n; ++i)
    {
        for(int j : pos[i])
        {
            ll val = (x[i] - x[j]) * (w[i] + w[j]);
            T.change(j, val);
        }
        for(pair<int, int> it : wen[i])
        {
            int l = it.first , id = it.second ;
            ans[id] = T.query(l);
        }
    }
    for(int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2022-11-20 21:39  梨愁浅浅  阅读(27)  评论(0编辑  收藏  举报