码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 5308 [ZJOI2018] 胖

时间:2019-01-05 20:06:19      阅读:193      评论:0      收藏:0      [点我收藏+]

标签:cst   不难   signed   you   define   va_arg   bre   ++   iostream   

bzoj 5308 [ZJOI2018] 胖

Solution

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

首先看数据范围,大概就是每次 \(O(k\log n)\) 或者 \(O(k\log ^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)\) 的时间内得到某个区间到某个点的路径长度的最小值,可以用线段树或者 \(\text{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\n", i, p1, L);
        }
        printf("%lld\n", ans);
    }
}

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

Review

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

bzoj 5308 [ZJOI2018] 胖

标签:cst   不难   signed   you   define   va_arg   bre   ++   iostream   

原文地址:https://www.cnblogs.com/wawawa8/p/10225500.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!