Closest Pair (树状数组+转换)

Closest Pair

[Link](Problem - F - Codeforces)

题意

​ 给你 n n n个点每个点有一个权重 w i w_i wi,定义两个点的权重和为 a b s ( x i − x j ) × ( w i + w j ) abs (x_i-x_j)\times (w_i+w_j) absxixj×(wi+wj),给你 m m m个询问,每次询问 [ l , r ] [l,r] [l,r]表示第 l l l到第 r r r个点里的最小点对权重和是多少。

思路

​ 首先有性质1:对于两个点如果他们中间有个比他们两个最大值要小的点这两个点就不会构成最小点对,因为新的点和权值较小的点会构成一个 w w w和更小且 d x dx dx更小的权值和。

​ 从前往后看每个点可以和他前面哪些点可能构成最小点对,对于一个点 x i x_i xi他只会和它前面比他 w w w大和第一个比他 w w w小的值构成最小点对,因为权值再小的点会和离他近的权值比 w i w_i wi小的点构成即不满足性质1,并且和 x i x_i xi构成的点不会和后面加进来的点构成最小点最,因为不如和 x i x_i xi构成的更小即不满足性质1。因此这样的点对最多有 O ( n ) O(n) O(n)个,我们设 p o s [ i ] pos[i] pos[i]为以 i i i结尾的和前面哪些点构成最小点对。

​ 对于询问我们也设成 q [ i ] q[i] q[i]询问区间 r r r i i i的询问,然后对于某一个询问 [ l , r ] [l,r] [l,r]我们要找的是到 [ l , r ] [l,r] [l,r]里的最小点 ,对于前面的点对如果 [ l i , r i ] [l_i,r_i] [li,ri]想要可以被算贡献一定有 l i ≥ l l_i\ge l lil,也就是对于每一个 l i l_i li他只会被小于他的 l l l算到贡献即它贡献的区间为 [ 1 , l i ] [1,l_i] [1,li],这里我们可以用树状数组来维护前缀最小值,但是树状数组可以维护的 [ l , n ] [l,n] [l,n],所以我们可以转换一下每个 l i l_i li维护 [ n + 1 − l i , n ] [n+1-l_i,n] [n+1li,n]的最小值,是等价的,然后查询 [ n + 1 − l i , n ] [n+1-l_i,n] [n+1li,n]就可以了。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 3e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1);
const LL inf = 8e18;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m, k;
LL tr[N];
LL x[N], w[N];
vector<pair<int, LL>> pos[N];
int lowbit(int x) {
    return x & -x;
}
void g(int a, int b) {
    pos[a].push_back({b, (x[a] - x[b]) * (w[a] + w[b])});
}
void add(int x, LL v) {
    for (; x <= n; x += lowbit(x)) tr[x] = min(tr[x], v);
}
LL query(int x) {
    LL s = inf;
    for (int i = x; i; i -= lowbit(i)) s = min(s, tr[i]);
    return s;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> m;    
    stack<int> stk;
    for (int i = 1; i <= n; i ++) {
        cin >> x[i] >> w[i];
        while (stk.size() && w[i] <= w[stk.top()]) {
            g(i, stk.top());
            stk.pop();
        }
        if (stk.size()) g(i, stk.top());

        stk.push(i);
    }
   
    vector<PII> q[n + 1];
    for (int i = 0; i < m; i ++) {
        int l, r;   cin >> l >> r;
        q[r].push_back({l, i});
    }

    for (int i = 1; i <= n; i ++) tr[i] = inf;
    
    vector<LL> res(m);
    for (int i = 1; i <= n; i ++) {
        for (auto t : pos[i]) add(n + 1 - t.x, t.y);
        for (auto t : q[i]) res[t.y] = query(n + 1 - t.x);
    }

    for (int i = 0; i < m; i ++) 
        cout << res[i] << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值