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;
}