bzoj 5308 [ZJOI2018] 胖
Link
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
想法比较自然,关键在于如何维护区间到某个点的距离的最值