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 ", ans); } return 0; }