题解:ARC159E Difference Sum Query

提供一个很简洁的做法。

对递归过程建出二叉树,那么 \(x_i\) 就是点 \(i\) 的深度。

对于两个点 \(i,j(i<j)\),不难观察到 \(\operatorname{lca}(i,j)\in[i,j]\),因此对于相邻的两个点 \(i,i+1\),必然有 \(\operatorname{lca}(i,i+1)\in\{i,i+1\}\)。若 \(i\) 是 \(i+1\) 的祖先,则从 \(i\) 走向 \(i+1\) 的路径形如先向右走 \(1\) 步,再向左走 \(k\) 步;若 \(i+1\) 是 \(i\) 的祖先,则从 \(i+1\) 走向 \(i\) 的路径形如先向左走 \(1\) 步,再向右走 \(k\) 步。这里 \(k+1=|x_i-x_{i+1}|\)。

考虑很精妙的一步,设 \(y_i\) 表示从根走到 \(i\) 的路径上,向左走的次数减去向右走的次数,那么直接有 \(y_{i+1}-y_i=k-1\),这样就把绝对值消掉了!

题目所求即为

\[\sum_{i=c}^{d-1}|x_i-x_{i+1}|=y_d-y_c+2(d-c) \]

题目限制等价于 \(\max\left(\dfrac{a_i}{a_i+b_i},\dfrac{b_i}{a_i+b_i}\right)\leq\dfrac{2}{3}\),因此对于长度为 \(l\) 的递归区间,不难分析出子区间的大小至多为 \(\left\lfloor\dfrac{2l}{3}\right\rfloor\),因此直接递归即可单次 \(\mathcal{O}(\log{n})\) 求出 \(y_i\)。

时间复杂度为 \(\mathcal{O}(q\log{n})\)。

代码非常好写。

代码
#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXM = 105;

template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
constexpr int lg2(ll x) { return 63 ^ __builtin_clzll(x); }
constexpr ll bitCeil(ll x) { return x == 1 ? 1ll : 1ll << lg2(x - 1) + 1; }

ll n;
int m, q, a[MAXM], b[MAXM];

int query(ll l, ll r, int d, ll x) {
	ll mid = (a[d % m] * l + b[d % m] * r) / (a[d % m] + b[d % m]);
	if (x == mid) return 0;
	return x < mid ? query(l, mid - 1, d + 1, x) + 1 : query(mid + 1, r, d + 1, x) - 1;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n >> m;
	for (int i = 0; i < m; ++i) cin >> a[i] >> b[i];
	cin >> q;
	while (q--) {
		ll c, d;
		cin >> c >> d;
		cout << query(1, n, 0, d) - query(1, n, 0, c) + (d - c << 1) << '\n';
	}
	return 0;
}
posted @ 2026-09-30 12:35  P2441M  阅读(10)  评论(0)    收藏  举报