CF671 E Organizing a Race (线段树更新)

题意

有n个点,n-1条边。每条边是 ( i , i + 1 ) (i,i+1) ,长度为 w [ i ] w[i] 。每走1长度要消耗1的油,每走到这个点上可以获得 g [ i ] g[i] 的油。
你有K次机会给某个点的 g [ i ] + 1 g[i]+1

求最大的满足下述条件的区间长度:
满足存在一个给油的方案,使可以从L不回头的走到R,也可以从R不回头的走到L。
n 1 0 5 , g , w , k 1 0 9 n\leq 10^5,g,w,k\leq10^9

思路 O ( n log 2 n ) O(n\log^2n)

  • 判断一个区间是否可行的方法是,从左到右走,走到每一个必须加的点就加,然后把剩下油的全部加到右端点,判断行不行。
  • 发现这个实际上形成了一个树形结构:L的父亲设为其初始状态往右走第一个走不到的点。到了这个点后油量是0,相当于从此处开始。
  • 在森林上遍历,考虑当前点L作为左端点时,如何判断每一个右端点是否可行:
  • 令:
  • p r e [ i ] = p r e [ i 1 ] w [ i 1 ] + g [ i ] pre[i]=pre[i-1]-w[i-1]+g[i]
  • s u f [ i ] = s u f [ i 1 ] w [ i ] + g [ i ] suf[i]=suf[i-1]-w[i]+g[i]
  • c o s t [ i ] cost[i] 表示从当前点至点 i i 总共需要额外加多少油。(注意不是走到点i所需要的额外油量!!!)
  • 从v的父亲u走到v时,需要在u-1处加上 s u f [ u 1 ] s u f [ v 1 ] suf[u-1]-suf[v-1] 的油,即相当于 c o s t [ u 1 ] c o s t [ n ] cost[u-1]到cost[n] 区间加
  • 注意, p r e pre 也要对应的改变。
  • 从R走回L所需的油是 m i n ( p r e [ R ] d e l t a [ R ] p r e [ i ] ) , i [ L , R ) -min(pre[R]-delta[R]-pre[i]), i\in[L,R)
  • p r e [ R ] + d e l t a [ R ] + m a x ( p r e [ i ] ) -pre[R]+delta[R]+max(pre[i]) .
  • d e l t a [ x ] delta[x] 是当前在x点加的油,之所以减掉是因为加在这个点的油是为了到R+1以后的点准备的。
  • 因此,判定条件为:
  • c o s t [ R 1 ] p r e [ R ] + d e l t a [ R ] + m a x ( p r e [ i ] ) cost[R-1]-pre[R]+delta[R]+max(pre[i])
  • = c o s t [ R ] p r e [ R ] + m a x ( p r e [ i ] ) , i [ L , R ) =cost[R]-pre[R]+max(pre[i]),i\in[L,R)
  • c o s t [ R 1 ] K cost[R-1]\leq K (因为走回来可能是没有意义的负代价)
  • 接下来用线段树维护这个东西。具体的维护方法类似维护单调栈。通过均衡维护的信息范围与更新分支数量来保证时间复杂度。
  • 参见官方题解
  • O ( n log 2 n ) O(n\log^2n)
  • 实际上我写的是一个上界 O ( n log 3 n ) O(n\log^3n) 的做法,不过做一些小改动就能变成两个log。(由此获得了更大的常数并慢了300ms)
  • 或许就是两个log的,但是不知道怎么分析…
#include <bits/stdc++.h>
#define max(a,b) ((a) > (b) ? (a) : (b))
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const ll inf = 1e18 + 50;
int n, K, nx[N];
ll g[N], w[N], pre[N], suf[N], out, maxp[N * 4], minc[N * 4], ans[N * 4];
ll fz[N * 4];
ll tag[N * 4];
vector<int> to[N];
stack<pair<ll,int>> S;
void build_tree() {
	for(int i = 1; i <= n; i++) {
		pre[i] = pre[i - 1] + g[i] - w[i - 1];
		suf[i] = suf[i - 1] + g[i] - w[i];
	}
	S.push(make_pair(0, 0));
	for(int i = 1; i < n; i++) {
		while (S.size() && S.top().first > suf[i]) {
			nx[S.top().second + 1] = i + 1;
			to[i + 1].push_back(S.top().second + 1);
			S.pop();
		}
		S.push(make_pair(suf[i], i));
	}
}
void putag(int x, ll v) {
	tag[x] += v;
	maxp[x] += v;
	ans[x] += v;
}

void down(int x) {
	if (tag[x] != 0) {
		putag(x << 1, tag[x]), putag(x << 1 | 1, tag[x]);
		tag[x] = 0;
	}
}

void calc(int x, int l, int r, ll pre_mx, ll &ret) {
	if (l == r) {
		ret = min(ret, minc[x] + pre_mx);
		return;
	}
	down(x);
	if (pre_mx >= maxp[x << 1]) {
		ret = min(ret, pre_mx + minc[x << 1]);
		calc(x << 1 | 1, (l + r >> 1) + 1, r, pre_mx, ret);
	} else {
		ret = min(ret, ans[x << 1 | 1]);
		calc(x << 1, l, l + r >> 1, pre_mx, ret);
	}
}

void update(int x, int l, int r) {
	maxp[x] = max(maxp[x << 1], maxp[x << 1 | 1]);
	fz[x] = fz[x << 1] + fz[x << 1 | 1];
	ans[x] = inf;
	calc(x << 1 | 1, (l + r >> 1) + 1, r, maxp[x << 1], ans[x]);
}

void build(int x, int l, int r) {
	if (l == r) {
		maxp[x] = pre[l];
		minc[x] = -pre[l];
		return;
	}
	build(x << 1, l, l + r >> 1);
	build(x << 1 | 1, (l + r >> 1) + 1, r);
	update(x, l, r);
	minc[x] = min(minc[x << 1], minc[x << 1 | 1]);
}

int LE;
void erfen(int x, int l, int r, ll pre_mx) {
	if (l == r) {
		if (pre_mx + minc[x] <= K)
			out = max(out, r - LE + 1);
		return;
	}
	down(x);
	if(minc[x << 1 | 1] + pre_mx <= K) {
		erfen(x << 1 | 1, (l + r >> 1) + 1, r, pre_mx);
	} else {
		erfen(x << 1, l, l + r >> 1, pre_mx);
	}
}

void _query(int x, int l, int r, ll pre_mx) {
	if (l == r) {
		if (pre_mx + minc[x] <= K) {
			out = max(out, r - LE + 1);
		}
		return;
	}
	down(x);
	if (pre_mx >= maxp[x << 1]) {
		erfen(x << 1, l, l + r >> 1, pre_mx);
		_query(x << 1 | 1, (l + r >> 1) + 1, r, pre_mx);
	} else {
		if (ans[x] <= K) {
			_query(x << 1 | 1, (l + r >> 1) + 1, r, maxp[x << 1]);
		} else {
			_query(x << 1, l, l + r >> 1, pre_mx);
		}
	}
}

void query(int x, int l, int r, int tl, int tr, ll &pre_mx) {
	if (tl <= l && r <= tr) {
		_query(x, l, r, pre_mx);
		pre_mx = max(pre_mx, maxp[x]);
		return;
	}
	if (l > tr || r < tl) return;
	down(x);
	query(x << 1, l, l + r >> 1, tl, tr, pre_mx);
	query(x << 1 | 1, (l + r >> 1) + 1, r, tl, tr, pre_mx);
}

void pre_change(int x, int l, int r, int tl, int tr, ll v) {
	if (tl <= l && r <= tr) {
		putag(x, v);
		return;
	}
	if (l > tr || r < tl) return;
	down(x);
	pre_change(x << 1, l, l + r >> 1, tl, tr, v);
	pre_change(x << 1 | 1, (l + r >> 1) + 1, r, tl, tr, v);
	update(x, l, r);
}

void fz_change(int x, int l, int r, int tg, ll v) {
	if (l == r) {
		fz[x] += v;
		return;
	}
	if (tg <= (l + r >> 1)) {
		fz_change(x << 1, l, l + r >> 1, tg, v);
	} else fz_change(x << 1 | 1, (l + r >> 1) + 1, r, tg, v);
	fz[x] = fz[x << 1] + fz[x << 1 | 1];
}

void find(int x, int l, int r, int rest, int& ri) {
	if (l == r) {
		if (rest >= fz[x]) ri = r;
		return;
	}
	down(x);
	if (rest >= fz[x << 1]) {
		ri = l + r >> 1;
		find(x << 1 | 1, (l + r >> 1) + 1, r, rest - fz[x << 1], ri);
	} else {
		find(x << 1, l, l + r >> 1, rest, ri);
	}
}

void dfs(int x) {
	LE = x;
	ll tmp = -inf;
	int ri = 0;
	find(1, 1, n, K, ri);
	query(1, 1, n, x, ri + 1, tmp);
	for(int y : to[x]) {
		ll d = suf[y - 1] - suf[x - 1];
		pre_change(1, 1, n, x - 1, n, d);
		fz_change(1, 1, n, x - 1, d);
		dfs(y);
		pre_change(1, 1, n, x - 1, n, -d);
		fz_change(1, 1, n, x - 1, -d);
	}
}

int main() {
	cin >> n >> K;
	for(int i = 1; i < n; i++) scanf("%lld", &w[i]);
	for(int i = 1; i <= n; i++) scanf("%lld", &g[i]);
	build_tree();
	build(1, 1, n);
	while(S.size()) {
		dfs(S.top().second + 1);
		S.pop();
	}
	cout << out << endl;
}
发布了266 篇原创文章 · 获赞 93 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/102898528