5308: [Zjoi2018]胖

5308: [Zjoi2018]胖

链接

分析:

  题目转化为一个点可以更新多少个点,一个点可以更新的点一定是一个区间,考虑二分左右端点确定这个区间。

  设当前点是x,向右二分一个点y,如果x可以更新到y,那么在x~y之间的所有关键点(存在宫殿往这边的点)到y的距离小于x到y的距离,以及y~2*y-x之间的关键点到y的距离小于x到y的距离。

  当然一个点可能同时被两个点更新,那么让最靠左的点更新它。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 200005;
int Log[N], n, m, k;
LL dis[N];
struct Node {
    int p; LL l;
    Node() {}
    Node(int _p,LL _l) { p = _p, l = _l; }
    bool operator < (const Node &A) const { return p < A.p; }
}a[N];
struct ST{
    LL f[20][N];
    void init() {
        for (int i = 1; i <= k; ++i) f[0][i] = a[i].l;
        for (int j = 1; j <= Log[k]; ++j) 
            for (int i = 1; i + (1 << j) - 1 <= k; ++i) 
                f[j][i] = min(f[j - 1][i], f[j - 1][i + (1 << (j - 1))]);
    }
    LL query(int l,int r) {
        l = max(1, l), r = min(r, n);
        l = lower_bound(a + 1, a + k + 1, Node(l, 0)) - a;
        r = upper_bound(a + 1, a + k + 1, Node(r, 0)) - a - 1;
        if (l > r) return 1e18;
        int k = Log[r - l + 1];
        return min(f[k][l], f[k][r - (1 << k) + 1]);
    }
}L, R;

bool check1(int x,int y) { // [2 * y - x + 1, y, x]
    if (x == y) return true;
    LL a = L.query(2 * y - x + 1, y) + dis[y];
    LL b = R.query(y, x - 1) - dis[y];
    LL c = R.query(x, x) - dis[y];
    if (a <= c || b <= c) return false;
    if (2 * y - x >= 1) return L.query(2 * y - x, 2 * y - x) + dis[y] > c; // 如果距离相同,优先给左边的点 
    return true;
}
int findL(int x) {
    int l = 1, r = x, ans = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check1(x, mid)) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    return ans;
}
bool check2(int x,int y) { // [x, y, 2 * y - x - 1]
    if (x == y) return true;
    LL a = L.query(x + 1, y) + dis[y];
    LL b = R.query(y, 2 * y - x - 1) - dis[y];
    LL c = L.query(x, x) + dis[y];
    if (a <= c || b <= c) return false;
    if (2 * y - x <= n) return R.query(2 * y - x, 2 * y - x) - dis[y] >= c;
    return true;
}
int findR(int x) {
    int l = x, r = n, ans = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check2(x, mid)) l = mid + 1, ans = mid;
        else r = mid - 1;
    }
    return ans;
}
int main() {
    n = read(), m = read();
    for (int i = 2; i <= n; ++i) Log[i] = Log[i >> 1] + 1;
    for (int i = 2; i <= n; ++i) dis[i] = dis[i - 1] + read();
    while (m --) {
        LL ans = 0;
        k = read();
        for (int i = 1; i <= k; ++i) a[i].p = read(), a[i].l = read();
        sort(a + 1, a + k + 1);
        for (int i = 1; i <= k; ++i) a[i].l -= dis[a[i].p]; L.init();
        for (int i = 1; i <= k; ++i) a[i].l += dis[a[i].p] * 2; R.init();
        for (int i = 1; i <= k; ++i) ans += findR(a[i].p) - findL(a[i].p) + 1;
        printf("%lld\n", ans);
    }
    return 0;
}

 

posted @ 2019-03-07 21:15  MJT12044  阅读(139)  评论(0编辑  收藏  举报