zoukankan      html  css  js  c++  java
  • BZOJ 5308 [ZJOI2018] Day2T2 胖 | 二分 ST表

    题目链接

    LOJ 2529

    BZOJ 5308

    题解

    这么简单的题
    为什么考场上我完全想不清楚 = =

    对于k个关键点中的每一个关键点(a),二分它能一度成为哪些点的最短路起点(显然这些点在一段包含(a)的连续区间中),所以二分这个区间的左右端点。

    如何判断某个点(p)是否在这个区间内呢?设(d = |a - p|),则(a)可以更新(p)当且仅当区间([p - d, p + d])中的关键点到(p)的距离没有比(a)更优的。

    (sum_i)(i)到点(1)的距离,用ST表维护一个关键点区间中((sum_i - l)_{max})((sum_i + l)_{min}),则([p - d, p + d])中对于(p)的最优关键点可能是([p - d, p])(sum_i - l)最大的或((p, p+d])(sum_i + l)最小的。将他俩与(a)比较,如果(a)仍然是最优的,则(a)可以一度占领(p)这个点。

    注意若两个关键点到(p)的距离相同,只能算一个,那么人为规定一下【离(p)边数最小的】是最优的,如果两个点这一项也一样,则取编号较小的即可。

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <iostream>
    #define enter putchar('
    ')
    #define space putchar(' ')
    using namespace std;
    typedef long long ll;
    template <class T>
    void read(T &x){
        char c;
        bool op = 0;
        while(c = getchar(), c < '0' || c > '9')
            if(c == '-') op = 1;
        x = c - '0';
        while(c = getchar(), c >= '0' && c <= '9')
            x = x * 10 + c - '0';
        if(op == 1) x = -x;
    }
    template <class T>
    void write(T x){
        if(x < 0) putchar('-'), x = -x;
        if(x >= 10) write(x / 10);
        putchar('0' + x % 10);
    }
    
    const int N = 200005;
    const ll INF = 1e18 + 233;
    int n, m, K, lg[N];
    ll sum[N], st1[N][20], st2[N][20], l1[N], l2[N];
    struct Edge {
        int a;
        ll l;
        Edge(){}
        Edge(int x, ll y): a(x), l(y){}
        bool operator < (const Edge &b) const {
            return a < b.a;
        }
    } e[N];
    
    int Max(int a, int b){
        if(l1[a] == l1[b]) return max(a, b);
        return l1[a] > l1[b] ? a : b;
    }
    int Min(int a, int b){
        if(l2[a] == l2[b]) return min(a, b);
        return l2[a] < l2[b] ? a : b;
    }
    void init(){
        sort(e + 1, e + K + 1);
        for(int i = 1; i <= K; i++){
            l1[i] = sum[e[i].a] - e[i].l, l2[i] = sum[e[i].a] + e[i].l;
            st1[i][0] = st2[i][0] = i;
        }
        for(int j = 1; (1 << j) <= K; j++)
            for(int i = 1; i + (1 << j) - 1 <= K; i++){
                st1[i][j] = Max(st1[i][j - 1], st1[i + (1 << (j - 1))][j - 1]);
                st2[i][j] = Min(st2[i][j - 1], st2[i + (1 << (j - 1))][j - 1]);
            }
    }
    int get1(int l, int r){
        int j = lg[r - l + 1];
        return Max(st1[l][j], st1[r - (1 << j) + 1][j]);
    }
    int get2(int l, int r){
        int j = lg[r - l + 1];
        return Min(st2[l][j], st2[r - (1 << j) + 1][j]);
    }
    int better(int a, int b, int p){
        ll ans1 = e[a].l + abs(sum[p] - sum[e[a].a]);
        ll ans2 = e[b].l + abs(sum[p] - sum[e[b].a]);
        if(ans1 != ans2) return ans1 < ans2 ? a : b;
        if(abs(e[a].a - p) != abs(e[b].a - p))
            return abs(e[a].a - p) < abs(e[b].a - p) ? a : b;
        return min(a, b);
    }
    bool bel(int p, int k){
        int d = abs(e[k].a - p), best = k;
        int l = lower_bound(e + 1, e + K + 1, Edge(p - d, 0)) - e;
        int r = upper_bound(e + 1, e + K + 1, Edge(p + d, 0)) - e - 1;
        int mid = upper_bound(e + 1, e + K + 1, Edge(p, 0)) - e - 1;
        if(l <= mid)  best = better(best, get1(l, mid), p);
        if(mid < r) best = better(best, get2(mid + 1, r), p);
        return best == k;
    }
    ll solve(){
        ll ans = 0;
        for(int i = 1; i <= K; i++){
            int l = 1, r = e[i].a, mid, L;
            while(l < r)
                if(bel(mid = (l + r) >> 1, i)) r = mid;
                else l = mid + 1;
            L = l, l = e[i].a, r = n;
            while(l < r)
                if(bel(mid = (l + r + 1) >> 1, i)) l = mid;
                else r = mid - 1;
            ans += (r - L + 1);
        }
        return ans;
    }
    
    int main(){
    
        for(int i = 2; i < N; i++)
            lg[i] = lg[i >> 1] + 1;
        read(n), read(m);
        for(int i = 2; i <= n; i++)
            read(sum[i]), sum[i] += sum[i - 1];
        while(m--){
            read(K);
            for(int i = 1; i <= K; i++)
                read(e[i].a), read(e[i].l);
            init();
            write(solve()), enter;
        }
    
        return 0;
    }
    
  • 相关阅读:
    (unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误
    HTTP KeepAlive模式
    Windows 7 中的 God Mode
    我的开发环境配置经验
    C#格式化数值结果表(格式化字符串)
    我可怜的笔记本电脑
    JetBrains ReSharper 5.x 注册机
    异常处理准则
    调用 Windows 7 中英文混合朗读
    oracle笔记(2010130)
  • 原文地址:https://www.cnblogs.com/RabbitHu/p/BZOJ5308.html
Copyright © 2011-2022 走看看