CF1406D Three Sequences

CF1406D Three Sequences

非常好性质题,使我的大脑旋转。

一个显然的性质

不难发现对于 \(\forall i \gt 1\),总满足 \(b_i = b_{i - 1}\)\(c_i = c_{i - 1}\)

进一步考虑发现当 \(a_i \gt a_{i - 1}\) 时,\(c_i = c_{i - 1}\),否则 \(b_i = b_{i - 1}\)

对于这种涉及相邻数大小关系的问题,我们通常考虑差分。但是接下来就不知道怎么下手了。

对数据的观察

考虑回到要解决的问题本身,结合上面的性质,很容易想到可以二分答案,并在 \(O(n)\) 的时间内检查合法性,具体判据为:令 \(c_1 = x\),跑一遍,查看是否存在一个 \(b_i \gt x\)。但这个做法非常劣,是 \(O(qn \log n)\) 的。

但在尝试各种答案的过程中,我惊讶地发现常常会出现 \(c_1 = b_n\) 的巧合,于是我又进一步地思考了这与上述所说“相邻数大小关系”之间的联系。于是发现答案 \(= \frac{c_1 + b_n}{2}\)

多观察数据可以发现这个性质,但我们接下来考虑一个稍微严谨一点的解释。

1、显然越往后 \(b_i \gt c_1\) 的可能性越大。

2、记 \(b_n = F(c_1)\)。容易理解,这个函数 \(F(x)\) 只与 \(a\) 的走势有关,因此对于每一个特定的询问,\(F(x)\) 是固定的。

那么,\(F(x)\) 是否具有单调性呢?答案是 \(F(x)\) 是单调递减的。考虑整个填数的过程:

\[b_i = \begin{cases} b_{i - 1} \qquad & a_i \le a_{i - 1} \\ a_i - c_{i - 1} \qquad & a_i \gt a_{i - 1} \end{cases} \]

考虑所有改变 \(b\) 的位置,得到

\(b_i = a_i - c_{i - 1} = a_i - (a_{i - 1} - b_{i - 2}) = a_i - (a_{i - 1} - (a_{i - 2} - c_{i - 3})) = \dots = a_i - a_{i - 1} + a_{i - 2} - \dots + a_1 - c_1\)

因此 \(F(x)\) 是单调递减的。

3、进一步地,\(\Delta b_n = - \Delta c_1\)

所以答案就是 \(c_1\)\(b_n\) 恰好相等或刚好相差 \(1\) 的时候,这也印证了上文中的巧合。

于是我们得到了一个 \(O(qn)\) 的做法,每次暴力修改,回答询问时取一个极小值 \(inf\),令 \(c_1 = inf\),最终答案就是 \(\lceil \frac{c_1 + b_n}{2} \rceil\)

结合差分

结合 \(b_i\) 的展开式,进一步化简答案式子,于是有

\[ans = \lceil \frac{a_i - a_{i - 1} + a_{i - 2} - \dots + a_1}{2} \rceil \]

所以 \(ans\) 的分母就是 \(a\) 的差分数组中所以大于 \(0\) 的数的总和。

而对差分数组区间修改只会影响两个端点的取值,所以该做法是 \(O(n + q)\) 的。

逆天细节:由于 C++ 除法是向 \(0\) 取整,所以分母 \(\ge 0\) 时先 \(+1\)\(/2\),分母 \(\lt 0\) 时直接 \(/2\)

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l); i <= (r); ++ i)
#define G(i,r,l) for(int i(r); i >= (l); -- i)
#define int ll 
using namespace std;
using ll = long long;
const int N = 2e5;
int n, q, sum = 0, a[N], d[N];
signed main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> n;
	F(i, 1, n){
		cin >> a[i];
		d[i] = a[i] - a[i - 1];
		if(i == 1 || d[i] > 0){
			sum += d[i];
		}
	} 
	cin >> q;
	F(o, 0, q){
		if(sum >= 0) cout << (sum + 1) / 2ll << '\n';
		else cout << sum / 2ll << '\n';
		if(o == q) return fflush(0), 0;
		int l, r, x;
		cin >> l >> r >> x;
			if(l > 1){
				if(d[l] > 0){
					if(d[l] + x <= 0) sum -= d[l];	
					else sum += x; 
				}
				else {
					if(d[l] + x > 0) sum += (d[l] + x);	
				}
			}
			else{
				sum += x;
			}
			d[l] += x;
			if(r < n){
				if(d[r + 1] > 0){
					if(d[r + 1] - x <= 0) sum -= d[r + 1];
					else sum -= x;
				}
				else{
					if(d[r + 1] - x > 0) sum += (d[r + 1] - x);	
				}
				d[r + 1] -= x;
			}
	}
	return fflush(0), 0;
}
posted @ 2025-08-10 17:05  superl61  阅读(9)  评论(0)    收藏  举报