CF765F Souvenirs 线段树

▼魔方 西西 提交于 2019-11-29 19:12:12

传送门


神仙题

将询问离线然后枚举右端点,维护每一个位置\(i\)与其之后的所有位置\(j\)的距离最小值然后区间取min。显然这样的做法的最劣复杂度是\(O(n^2)\)的,我们需要一些剪枝。

我们考虑对于某一个位置\(i\),在其之后真正有可能更新最小值的位置有哪些。如果设序列\(\{a\}\)是一个递增序列,\(a_1 > i,val_{a_1} \geq val_i\),且每当右端点取到其中的某个\(a_j\)\(i\)位置的最小值会进行有效的更新,那么会要满足:\(\forall j \in [1,|a|) , |val_i - val_{a_j}| \geq 2|val_i - val_{a_{j+1}}|\),这是因为如果这个条件不满足那么\(a_j\)\(a_{j+1}\)的距离会更小,此时对\(i\)的更新就是无效的。

不难发现上面的\(a\)序列的长度最多只会有\(log 10^9\)项,所以我们可以使用线段树找出当右端点移到哪些位置的时候需要修改\(i\)位置,然后移动右端点进行更新并统计答案。复杂度\(O(nlog^2n+qlogn)\)

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

int read(){
    int a = 0; char c = getchar(); bool f = 0;
    while(!isdigit(c)){f = c == '-'; c = getchar();}
    while(isdigit(c)){
        a = a * 10 + c - 48; c = getchar();
    }
    return f ? -a : a;
}

const int _ = 3e5 + 7;
int cnt , N , M , arr[_] , lsh[_] , id[_] , L[_] , R[_] , ans[_]; vector < int > Q[_] , pos[_];

namespace segt{
    int mn[_ << 2];

#define mid ((l + r) >> 1)
#define lch (x << 1)
#define rch (x << 1 | 1)
    
    void clear(){memset(mn , 0x3f , sizeof(mn));}
    
    void modify(int x , int l , int r , int tar , int val){
        mn[x] = min(mn[x] , val); if(l == r) return;
        mid >= tar ? modify(lch , l , mid , tar , val) : modify(rch , mid + 1 , r , tar , val);
    }
    
    int qry(int x , int l , int r , int L , int R){
        if(l >= L && r <= R) return mn[x];
        int mn = 1e9;
        if(mid >= L) mn = qry(lch , l , mid , L , R);
        if(mid < R) mn = min(mn , qry(rch , mid + 1 , r , L , R));
        return mn;
    }
}using namespace segt;

int main(){
    N = read(); segt::clear(); for(int i = 1 ; i <= N ; ++i) arr[i] = lsh[i] = read();
    M = read(); for(int i = 1 ; i <= M ; ++i){L[i] = read(); R[i] = read(); Q[R[i]].push_back(i);}
    sort(lsh + 1 , lsh + N + 1); cnt = unique(lsh + 1 , lsh + N + 1) - lsh - 1;
    for(int i = 1 ; i <= N ; ++i) id[i] = lower_bound(lsh + 1 , lsh + cnt + 1 , arr[i]) - lsh;
    for(int i = N , cur , t ; i ; --i){
        cur = cnt;
        while((t = segt::qry(1 , 1 , cnt , id[i] , cur)) <= N){
            pos[t].push_back(i); if(id[t] == id[i]) break;
            cur = upper_bound(lsh + 1 , lsh + cnt + 1 , (arr[i] + arr[t]) / 2) - lsh - 1;
        }
        cur = 1;
        while((t = segt::qry(1 , 1 , cnt , cur , id[i])) <= N){
            pos[t].push_back(i); if(id[t] == id[i]) break;
            cur = lower_bound(lsh + 1 , lsh + cnt + 1 , (arr[i] + arr[t] + 1) / 2) - lsh;
        }
        segt::modify(1 , 1 , cnt , id[i] , i);
    }
    segt::clear();
    for(int i = 1 ; i <= N ; ++i){
        for(auto t : pos[i]) segt::modify(1 , 1 , N , t , abs(arr[i] - arr[t]));
        for(auto t : Q[i]) ans[t] = segt::qry(1 , 1 , N , L[t] , i);
    }
    for(int i = 1 ; i <= M ; ++i) printf("%d\n" , ans[i]);
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!