CF1172F Nauuo and Bug

题目链接

只想到一种离线后需要支持区间加,大于 \(p\) 的值减 \(p\),单点查操作的做法,但是并不会。

考虑到一个区间减 \(p\) 的次数是有限的,即 \(cnt \in [0,len]\)。如果我们能够对每个 \(cnt\) 求出传入哪个区间的数能够恰好减 \(cnt\)\(p\) 的话,就可以快速求出 \(x\) 出进去后传出的是什么了。

考虑用线段树维护。设 \(c_x\) 表示经过当前节点的区间要想减 \(x\)\(p\) 的最小的传入值,由此可以推出减 \(x\) 次对应的值的区间(\(c_x...c_{x+1}-1\))。

考虑如何合并两个节点。我们有一个 \(O(len_l \times len_r)\) 的做法:(是取最小值)

\[c_{now,x+y} \gets max(c_{l,x},c_{r,y}+xp-sum_{l}) \]

(需要同时符合大于等于 \(c_x\) 和大于等于 \(c_y+xp-sum_l\) 的条件)

显然这个是可以优化的。假设我们外层循环 \(x\),内层循环 \(y\)。发现 \(y\) 大到某种程度的时候,左区间的减 \(x\) 次的范围中最大的那个数都不能让右区间的减的次数达到 \(y\),那么当 \(y\) 更大的时候显然也是不可行的(不合法,可忽略),于是可以直接退出。

还可以发现,当 \(x\) 增加一, \(y\) 减少一的时候一定不会更优。考虑这种情况发生后,\(c_x\) 增大至少 \(p\)\(c_y+xp-sum_l\) 减少至少 \(0\)。如果原来 \(c_x\) 较大,那么现在显然不会更优。如果原来右边较大,那么原来肯定是 \(c_x ... c_{x+1}-1\) 中有一个数传进去后落到了 \(c_y\)(否则不合法可忽略,之前就不会到那里),如果现在 \(c_x\) 较大,不优(同上);如果现在 \(c_y+xp-sum_l\) 较大,那么一定是 \(c_x...c_{x+1}-1\) 中有一个数传进去后落到了 \(c_y\),而现在 \(x\) 增大了一,\(y\) 减小了一,显然是不可能的。

于是可以用指针扫描 \(O(len)\) 合并。

关键代码:

void build(int L, int R, int &cur) {
	cur = ++ttot; sm[cur] = sum[R] - sum[L - 1];
	if (L == R) {
		vec[cur].push_back(-inf);
		vec[cur].push_back(p - sm[cur]);
		vec[cur].push_back(inf);
		return ;
	}
	int mid = (L + R) >> 1; build(L, mid, ls[cur]); build(mid + 1, R, rs[cur]);
	for (int i = 0; i <= R - L + 2; ++i)	vec[cur].push_back(inf);
	vec[cur][0] = -inf;
	int ptr = 0, Ls = ls[cur], Rs = rs[cur];
	for (int i = 0; i <= mid - L + 1; ++i) {
		while (1) {
			ll tmp = vec[Ls][i + 1] - 1 + sm[Ls] - 1ll * i * p;
			if (tmp < vec[Rs][ptr]) {
				if (ptr) --ptr; break;
			}
			MIN(vec[cur][i + ptr], max(vec[Ls][i], vec[Rs][ptr] + 1ll * i * p - sm[Ls]));
			if (ptr + 1 > R - mid)	break;
			++ptr;
		}
	}
}

ll query(int L, int R, int l, int r, ll x, int cur) {
	if (l <= L && R <= r) {
		int cnt = upper_bound(vec[cur].begin(), vec[cur].end(), x) - vec[cur].begin() - 1;
		return x - 1ll * cnt * p + sm[cur];
	}
	int mid = (L + R) >> 1;
	if (l <= mid && r > mid) {
		x = query(L, mid, l, r, x, ls[cur]);
		return query(mid + 1, R, l, r, x, rs[cur]);
	}
	if (l <= mid)	return query(L, mid, l, r, x, ls[cur]);
	return query(mid + 1, R, l ,r, x, rs[cur]);
}
posted @ 2020-09-16 19:03  JiaZP  阅读(174)  评论(0编辑  收藏  举报