bzoj 5308 [ZJOI2018] 胖

Posted wawawa8

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 5308 [ZJOI2018] 胖相关的知识,希望对你有一定的参考价值。

bzoj 5308 [ZJOI2018] 胖

Solution

( ext{ZJOI2018}) 最简单的一道题

首先看数据范围,大概就是每次 (O(klog n)) 或者 (O(klog ^2n))

不难发现,答案就是每一个修建方案对应的点能够扩展的点数之和

就是说,对于每一个修建方案对应的点,它能够在 贝尔福特曼 算法中扩展到的点是一个区间,我们要求所有这样的区间的长度的和

因为是一个区间,所以我们可以二分它的左右端点,关键在于如何判断当前位置能否被当前点扩展到

假设当前点是 (a_i),当前位置是 (pos),令 (d= |a_i-pos|),那么影响到的区间为 ([a_i-d,a_i+d]),如果这个区间里面存在一个 (p),满足 (dis(p,pos) < dis(a_i,pos)),那么 (p) 肯定会优先扩展到 (pos),所以我们需要在 (O(log n)) 的时间内得到某个区间到某个点的路径长度的最小值,可以用线段树或者 ( ext{ST}) 表完成,只需要维护区间到两个端点的路径长度最小值即可

注意如果多个点到一个位置的路径长度相同,我们需要让这个位置只被算一次,所以让这个位置被经过边数最少的点扩展,如果还有多个,我们让它被左侧的点扩展

Code

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__)

ll read(){
    ll x=0,f=1;char c=getchar();
    while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

const int maxn = 200200;
int n, m, k;
ll a[maxn], p[maxn], le[maxn];
ll mn1[maxn][20], mn2[maxn][20];
pii pp[maxn];
int lg[maxn];

inline ll dis(int x, int y) {
    if (x > y) return 1e18;
    return a[y - 1] - a[x - 1];
}
inline ll calc1(int l, int r) {
    if (r < l) return 1e18;
    int len = lg[r - l + 1];
    int pos = l + (1 << len) - 1, pos2 = r - (1 << len) + 1;
    return min(mn1[l][len] + dis(p[pos], p[r]), mn1[pos2][len]);
}
inline ll calc2(int l, int r) {
    if (r < l) return 1e18;
    int len = lg[r - l + 1];
    int pos = r - (1 << len) + 1;
    return min(mn2[l][len], mn2[pos][len] + dis(p[l], p[pos]));
}
inline ll cal1(int l, int r) {
    int pos1 = lower_bound(p + 1, p + k + 1, l) - p;
    int pos2 = upper_bound(p + 1, p + k + 1, r) - p - 1;
    if (pos2 < pos1) return 1e18;
    return calc1(pos1, pos2) + dis(p[pos2], r);
}
inline ll cal2(int l, int r) {
    int pos1 = lower_bound(p + 1, p + k + 1, l) - p;
    int pos2 = upper_bound(p + 1, p + k + 1, r) - p - 1;
    if (pos2 < pos1) return 1e18;
    return calc2(pos1, pos2) + dis(l, p[pos1]);
}
inline bool pd1(int x, int c) {
    if (c < 1 || c > n) return false;
    int pos = p[x], d = pos - c, l = c - d, r = c + d;
    ll zuo = cal1(l, c), you = cal2(c, r - 1);
    ll dist = min(zuo, you), nw = dis(c, pos) + le[x];
    if (nw < dist) return true;
    else return false;
}
inline bool pd2(int x, int c) {
    if (c < 1 || c > n) return false;
    int pos = p[x], d = c - pos, l = c - d, r = c + d;
    ll zuo = cal1(l + 1, c), you = cal2(c, r - 1), you2 = cal2(c, r);
    ll dist = min(zuo, you2), nw = dis(pos, c) + le[x];
    if (nw < dist) return true;
    else if (nw == dist) {
        ll dist2 = min(zuo, you);
        if (nw == dist2) return false;
        else return true;
    }
    else return false;
}
void work(){
    n = read(), m = read();
    rep(i, 1, n - 1) a[i] = read(), a[i] += a[i - 1];
    rep(i, 2, n) lg[i] = lg[i >> 1] + 1;
    while (m--) {
        k = read();
        rep(i, 1, k) {
            int pos = read(), l = read();
            pp[i] = mp(pos, l);
        }
        sort(pp + 1, pp + k + 1);
        rep(i, 1, k) {
            p[i] = pp[i].fi, le[i] = pp[i].se;
            mn1[i][0] = le[i]; mn2[i][0] = le[i];
        }
        rep(j, 1, 18) {
            rep(i, 1, k) {
                if (i + (1 << j) - 1 > k) break;
                int d = i + (1 << (j - 1)), e = i + (1 << j) - 1;
                mn1[i][j] = min(mn1[i][j - 1] + dis(p[d - 1], p[e]), mn1[d][j - 1]);
                mn2[i][j] = min(mn2[i][j - 1], mn2[d][j - 1] + dis(p[i], p[d]));
            }
        }
        ll ans = 0;
        rep(i, 1, k) {
            int L = 1, R = p[i];
            while (L + 1 < R) {
                int md = (L + R) >> 1;
                if (pd1(i, md)) R = md;
                else L = md + 1;
            }
            if (pd1(i, R - 1)) R--;
            int p1 = R;
            L = p[i], R = n;
            while (L + 1 < R) {
                int md = (L + R) >> 1;
                if (pd2(i, md)) L = md;
                else R = md - 1;
            }
            if (pd2(i, L + 1)) L++;
            ans += L - p1 + 1;
            // printf("%d %d %d
", i, p1, L);
        }
        printf("%lld
", ans);
    }
}

int main(){
    #ifdef LZT
        freopen("in","r",stdin);
    #endif
    
    work();
    
    #ifdef LZT
        Debug("My Time: %.3lfms
", (double)clock() / CLOCKS_PER_SEC);
    #endif
}

Review

想法比较自然,关键在于如何维护区间到某个点的距离的最值

以上是关于bzoj 5308 [ZJOI2018] 胖的主要内容,如果未能解决你的问题,请参考以下文章

zjoi[ZJOI2018]胖

ZJOI 2018 Day 2 胖

bzoj5213: [Zjoi2018]迷宫

ZJOI2018未完成

bzoj3111: [Zjoi2013]蚂蚁寻路

bzoj 1433: [ZJOI2009]假期的宿舍