bzoj 5308 [ZJOI2018] 胖

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

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

posted @ 2019-01-05 17:53  wawawa8  阅读(189)  评论(2编辑  收藏  举报