2025牛客暑期多校训练营4


B. Blind Alley

题意:在一个迷宫里,从\((1, 1)\)出发,只能向上向下和向右走,且不能走墙。目的是走到\((1, m)\)。如果走到了第\(j\)列,那么可以看到\([j, \min(j + k, m)]\)这些列的状态。求有没有一个位置,使得其不能到达终点,但从起点到这个点的路径上除了\((i, j)\)的所有点的视野都无法判断这个点不能到达终点。

\(r[i][j]\)\((i, j)\)可以到的最右边的列,那么如果\((i, j)\)可以从\((1, 1)\)到达,且\(r[i][j] \geq j + k\)。则可以走到\((i, j)\)这个位置,而不知道这个位置不能到终点。
\(r[i][j]\)可以预处理,从后往前\(dp\)就行。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, m, k;
	std::cin >> n >> m >> k;
	std::vector<std::string> s(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> s[i];
	}

	std::vector r(n, std::vector<int>(m));
	for (int i = 0; i < n; ++ i) {
		r[i][m - 1] = m - 1;
	}

	for (int j = m - 2; j >= 0; -- j) {
		for (int i = 0; i < n; ++ i) {
			r[i][j] = j;
		}

		for (int i = 0; i < n; ++ i) {
			if (s[i][j] == '0') {
				r[i][j] = std::max(r[i][j], r[i][j + 1]);
			}
		}

		for (int i = 1; i < n; ++ i) {
			if (s[i][j] == '0') {
				r[i][j] = std::max(r[i][j], r[i - 1][j]);
			}
		}

		for (int i = n - 2; i >= 0; -- i) {
			if (s[i][j] == '0') {
				r[i][j] = std::max(r[i][j], r[i + 1][j]);
			}
		}
	}
	
	std::vector f(n, std::vector<int>(m));
	for (int i = 0; i < n; ++ i) {
		f[i][0] = 1;
	}
	
	for (int j = 1; j + k - 1 < m; ++ j) {
		for (int i = 0; i < n; ++ i) {
			if (s[i][j] == '0') {
				f[i][j] |= f[i][j - 1];
			}
		}

		for (int i = 1; i < n; ++ i) {
			if (s[i][j] == '0') {
				f[i][j] |= f[i - 1][j];
			}
		}

		for (int i = n - 2; i >= 0; -- i) {
			if (s[i][j] == '0') {
				f[i][j] |= f[i + 1][j];	
			}
		}

		for (int i = 0; i < n; ++ i) {
			if (s[i][j] == '0' && f[i][j] && r[i][j] >= j + k && r[i][j] < m - 1) {
				std::cout << "Yes\n";
				return;
			}
		}
	}

	std::cout << "No\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

D. Determinant of 01-Matrix

赛后补题
题意:构造一个行列式,使得它的值为\(n\)

构造太难,而且线代早忘了。。。
参考题解,大意就是第一行作为符号,然后下面构造一些等于\(2\)和等于\(1\)的位置,就可以用二进制一位一位构造出\(n\)来。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

const int N = 29;
int a[210][210];

void solve() {
	for (int i = 0; i < N; ++ i) {
		a[4 * i + 1][4 * i] = 1;
		a[4 * i + 1][4 * i + 1] = 1;
		a[4 * i + 2][4 * i + 1] = 1;
		a[4 * i + 2][4 * i + 2] = 1;
		a[4 * i + 3][4 * i] = 1;
		a[4 * i + 3][4 * i + 2] = 1;

		a[4 * i + 3][4 * i + 3] = 1;
		a[4 * i + 4][4 * i + 3] = 1;
		a[4 * i + 4][4 * i + 4] = 1;
	}

	int n;
	std::cin >> n;
	for (int i = 0; i <= N; ++ i) {
		if (n >> i & 1) {
			a[0][4 * i] = 1;
		}
	}

	std::cout << 4 * N + 1 << "\n";
	for (int i = 0; i <= 4 * N; ++ i) {
		for (int j = 0; j <= 4 * N; ++ j) {
			std::cout << a[i][j] << " \n"[j == 4 * N];
		}
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

E. Echoes of 24

题意:一棵树,每个点有权值。有两种操作,一种是单点修改权值,一种是查询一条路径上,依次对于每个数选择加或乘能否得到\(24\)这个数字。

如果两个点距离小于等于\(24\),直接把这条路径搞出来然后\(dp\)就行了。否则,我们发现如果不等于\(1\)的值的和小于等于\(24\),就一定可以,因为我们可以把这些数全加上,不够就继续加一,够了就乘一。那么我们需要知道树上路径的和,再加上有修改操作,可以用树链剖分加单点修改区间查询的数据结构维护。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

struct HLD {
	std::vector<int> in, out, size, deep, fa, top, idx;
	std::vector<std::vector<int>> adj;
	int n, dfn;
	HLD(){}
	HLD(int _n) {
		init(_n);
	}

	void init(int _n) {
		n =_n;
		in.resize(n);
		out.resize(n);
		size.resize(n);
		deep.resize(n);
		fa.resize(n);
		top.resize(n);
		idx.resize(n);
		dfn = 0;
		adj.assign(n, {});
	}

	void addEdge(int u, int v) {
		adj[u].push_back(v);
		adj[v].push_back(u);
	}

	void work(int root = 0) {
		top[root] = root;
		fa[root] = -1;
		deep[root] = 0;
		dfs1(root);
		dfs2(root);
	}

	void dfs1(int u) {
		if (fa[u] != -1) {
			adj[u].erase(std::find(adj[u].begin(), adj[u].end(), fa[u]));
		}

		size[u] = 1;
		for (int i = 0; i < adj[u].size(); ++ i) {
			int v = adj[u][i];
			fa[v] = u;
			deep[v] = deep[u] + 1;
			dfs1(v);
			size[u] += size[v];
			if (size[v] > size[adj[u][0]]) {
				std::swap(adj[u][0], adj[u][i]);
			}
		}
	}

	void dfs2(int u) {
		idx[dfn] = u;
		in[u] = dfn ++ ;
		for (auto & v : adj[u]) {
			top[v] = v == adj[u][0] ? top[u] : v;
			dfs2(v);
		}

		out[u] = dfn - 1;
	}

	int lca(int u, int v) {
		while (top[u] != top[v]) {
			if (deep[top[u]] < deep[top[v]]) {
				std::swap(u, v);
			}

			u = fa[top[u]];
		}

		return deep[u] < deep[v] ? u : v;
	}

	int dist(int u, int v) {
		return deep[u] + deep[v] - 2 * deep[lca(u, v)];
	}
};

template <class Info>
struct SegmentTree {
	struct Node {
		int l, r;
		Info info;
	};

	std::vector<Node> tr;

	SegmentTree() {};
	SegmentTree(int n) {
		init(n);
	}

	SegmentTree(std::vector<Info> & info) {
		init(info);
	}

	void init(int n) {
		tr.assign(n << 2, {});
		build(0, n - 1);
	}

	void init(std::vector<Info> & info) {
		int n = info.size();
		tr.assign(n << 2, {});
		std::function<void(int, int, int)> build = [&](int l, int r, int u) -> void {
			tr[u] = {l, r, {}};
			if (l == r) {
				tr[u].info = info[l];
				return;
			}

			int mid = l + r >> 1;
			build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
			pushup(u);
		};

		build(0, n - 1, 1);
	}

	void pushup(int u) {
		tr[u].info = tr[u << 1].info + tr[u << 1 | 1].info;
	}

	void build(int l, int r, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
		pushup(u);
	}

	void modify(int p, const Info & info, bool set = false) {
		int u = 1;
		while (tr[u].l != tr[u].r) {
			int mid = tr[u].l + tr[u].r >> 1;
			if (p <= mid) {
				u = u << 1;
			} else {
				u = u << 1 | 1;
			}
		}

		if (set) {
			tr[u].info = info;
		} else {
			tr[u].info = tr[u].info + info;
		}

		u >>= 1;
		while (u) {
			pushup(u);
			u >>= 1;
		}
	}

	Info query(int l, int r, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			return tr[u].info;
		}

		int mid = tr[u].l + tr[u].r >> 1;
		if (r <= mid) {
			return query(l, r, u << 1);
		} else if (l > mid) {
			return query(l, r, u << 1 | 1);
		}

		return query(l, r, u << 1) + query(l, r, u << 1 | 1);
	}
};

struct Info {
	int sum;
};

Info operator + (const Info & l, const Info & r) {
	Info res{};
	res.sum = l.sum + r.sum;
	return res;
}

void solve() {
	int n, q;
	std::cin >> n >> q;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	HLD tr(n);
	for (int i = 1; i < n; ++ i) {
		int u, v;
		std::cin >> u >> v;
		-- u, -- v;
		tr.addEdge(u, v);
	}

	tr.work();
	std::vector<Info> info(n);
	for (int i = 0; i < n; ++ i) {
		info[tr.in[i]].sum = a[i] > 1 ? std::min(25, a[i]) : 0;
	}

	SegmentTree<Info> segtr(info);

	auto sum = [&](int u, int v) -> int {
		Info res{};
		while (tr.top[u] != tr.top[v]) {
			if (tr.deep[tr.top[u]] < tr.deep[tr.top[v]]) {
				std::swap(u, v);
			}

			res = res + segtr.query(tr.in[tr.top[u]], tr.in[u]);
			u = tr.fa[tr.top[u]];
		}

		if (tr.deep[u] > tr.deep[v]) {
			std::swap(u, v);
		}

		res = res + segtr.query(tr.in[u], tr.in[v]);
		return res.sum;
	};

	auto dp = [&](int u, int v) -> int {
		int fa = tr.lca(u, v);
		std::vector<int> b, c;
		while (u != fa) {
			b.push_back(u);
			u = tr.fa[u];
		}

		b.push_back(fa);
		
		while (v != fa) {
			c.push_back(v);
			v = tr.fa[v];
		}

		b.insert(b.end(), c.rbegin(), c.rend());
		if (a[b[0]] > 24) {
			return 0;
		}
		std::array<bool, 25> f{};
		f[a[b[0]]] = true;
		b.erase(b.begin());
		for (auto & x : b) {
			std::array<bool, 25> g{};
			for (int i = 1; i <= 24; ++ i) {
				if (i + a[x] <= 24) {
					g[i + a[x]] |= f[i];
				}

				if (1LL * i * a[x] <= 24) {
					g[i * a[x]] |= f[i];
				}
			}

			f = g;
		}

		return f[24];
	};

	while (q -- ) {
		int op, x, y;
		std::cin >> op >> x >> y;
		if (op == 1) {
			-- x, -- y;
			if (tr.dist(x, y) > 24) {
				std::cout << (sum(x, y) <= 24) << "\n";
			} else {
				std::cout << dp(x, y) << "\n";
			}
		} else {
			-- x;
			a[x] = y;
			segtr.modify(tr.in[x], Info{y == 1 ? 0 : std::min(25, y)}, true);
		}
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

F. For the Treasury!

题意:一个数组取前\(k\)个数的和为价值。你每次可以移动相邻两个数,代价为\(c\),求可以得到的最大价值。

如果想让第\(i\)个数移动后面去,让\(j\)从后面进来,可以看作\(j\)移动到\(k+1\),然后\(i\)也移动到\(k+1\)。那么记原本价值为\(sum\),对于\(i\leq k\),移动第\(i\)个数代价为\(a[i] + (k-i+1)\times c\);对于\(i>k\),贡献为\(a[i] - (i-k+1)\times c\)。我们每次选一对交换,选前面代价最小的和后面贡献最大的。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, k;
	i64 c;
	std::cin >> n >> k >> c;
	std::vector<i64> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	std::vector<i64> A, B;
	i64 ans = std::accumulate(a.begin(), a.begin() + k, 0ll);
	for (int i = 0; i < k; ++ i) {
		A.push_back(a[i] + (k - i) * c);
	}

	for (int i = k; i < n; ++ i) {
		B.push_back(a[i] - (i - k) * c);
	}

	std::ranges::sort(A);
	std::ranges::sort(B, std::greater<>());
	i64 sum = ans;
	for (int i = 0; i < A.size() && i < B.size(); ++ i) {
		sum += -A[i] + B[i];
		ans = std::max(ans, sum);
	}
	std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

G. Ghost in the Parentheses

赛后补题。
题意:一个括号序列,每个位置可能变成未知的,对于未知的地方随便填,求有多少情况只能填出来原序列这一种合法的括号序列。

经典技巧是把'('看作\(1\),')'看作\(-1\),那么一个合法括号序列的充要条件是,每一个前缀都大于等于\(0\)且总和为\(0\)
考虑有哪些位置未知只能填出原序列。
满足条件的序列有两个条件:

  1. 左边若干个'('和右边若干个')'组成
  2. 交换最后的'('和最前的')'会使得没有括号序列合法。

如果不满足条件一,则交换一个')'和在它左边的'(',只会使得一些前缀和变大,那么得到的序列依然合法,也就是可以得到其它合法的序列。
考虑条件二的情况是\(((((...))))\)的情况,只要交换最后的')'和最前的'('非法,则只能填出原序列。因为拿一个更前的'('来交换,影响的前缀更多,更容易出现不合法的情况,而我们考虑的是有没有一种方式导致这个序列合法,也就是会有导致合法序列出现,那么希望观察影响前缀最小的情况,如果这个情况也无法产生其它的合法序列,则只能填出来原序列。
有人可能不懂为什么要交换两个括号,因为我们希望选出来的序列随便填也只能得到原序列一种,那么显然左括号右括号个数和原来不一样的填法肯定不合法,那么如果个数还是和原来一样,就可以看作有些括号进行了交换,显然交换相同括号没有意义,所以我们讨论交换左右括号的贡献。
那么对于交换的一对\(i, j\)\(i\)的位置本来贡献是\(1\),现在贡献是\(-1\),那么\([i, j)\)这一段的前缀和都要减\(2\),则只需要存在一个\(k\in [i, j)\),满足\(sum[k] < 2\)就行。
实际实现中,我们只需要枚举\(k\)就行了,那么记\(pre, suf\)分别为前缀左括号个数和后缀右括号个数,则有\(2^{pre_k} \times 2^{suf_{k+1}}\)中选法,但发现不同的\(k\)会重复计算一些区间,可以记\(pre\_cnt\)为上一个\(k\)的左括号的个数,\(now\)为上一个\(k\)到当前\(k\)之间的左括号的个数,然后我们不计算全不选的情况,贡献为\(2^{pre\_cnt} \times (2^{now}- 1) \times 2^{suf_{k+1}}\)。最后我们这里是没有计算全是右括号的情况的,以及需要加上一个不选的情况,那么需要再加上\(2^{\frac{n}{2}}\)
代码省略取模类。

点击查看代码
constexpr int P = 998244353;
using Z = MInt<P>;

void solve() {
    std::string s;
    std::cin >> s;
    int n = s.size();
    std::vector<int> sum(n + 1);
    for (int i = 0; i < n; ++ i) {
        sum[i + 1] = sum[i] + (s[i] == '(' ? 1 : -1);
    }

    std::vector<int> suf(n + 2);
    for (int i = n - 1; i >= 0; -- i) {
        suf[i + 1] = suf[i + 2] + (s[i] == ')');
    }

    std::vector<Z> p(n + 1);
    p[0] = 1;
    for (int i = 1; i <= n; ++ i) {
        p[i] = p[i - 1] * 2;
    }

    Z ans = 0;
    int pre = 0, now = 0;
    for (int i = 1; i <= n; ++ i) {
        now += s[i - 1] == '(';
        if (sum[i] < 2) {
            ans += p[pre] * (p[now] - 1) * p[suf[i + 1]];
            pre += now;
            now = 0;
        }
    }

    ans += p[n / 2];
    std::cout << ans / p[n] << "\n";
}

int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    int t = 1;
    // std::cin >> t;
    while (t -- ) {
        solve();
    }
    return 0;
}

I. I, Box

题意:推箱子,不过箱子可以自己左右移动。

无解的情况就是一个联通块里箱子和目标个数不一样。
然后就是暴力模拟了,我是用三个\(set\)分别存没到目标的箱子的位置、没有箱子的目标位置、有箱子的目标位置。
然后每次从第二个集合里拿一个\(bfs\)找第一个集合里最近的。记录路径然后模拟,如果遇到第三个集合的位置,则总体往前推,再从最后一个箱子继续推。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

struct DSU {
	std::vector<int> fa, cnt;
	DSU();
	DSU(int n) {
		init(n);
	}

	void init(int n) {
		fa.assign(n + 1, 0);
		cnt.assign(n + 1, 1);
		std::ranges::iota(fa, 0);
	}

	int find(int x) {
		return x == fa[x] ? x : fa[x] = find(fa[x]);
	}

	bool merge(int x, int y) {
		int u = find(x), v = find(y);
		if (u == v) {
			return false;
		}

		fa[v] = u;
		cnt[u] += cnt[v];
		return true;
	}

	bool same(int u, int v) {
		return find(u) == find(v);
	}

	int size(int u) {
		return cnt[find(u)];
	}
};

const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int st[55][55];
std::pair<int, int> pre[55][55];

void solve() {
	int n, m;
	std::cin >> n >> m;
	std::vector<std::string> s(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> s[i];
	}
	std::set<std::pair<int, int>> a, b, c;
	DSU dsu(n * m);
	for (int i = 0; i < n; ++ i) {
		for (int j = 0; j < m; ++ j) {
			if (s[i][j] == '@') {
				a.emplace(i, j);
			} else if (s[i][j] == '*') {
				b.emplace(i, j);
			} else if (s[i][j] == '!') {
				c.emplace(i, j);
			}

			if (s[i][j] == '#') {
				continue;
			}

			for (int k = 0; k < 4; ++ k) {
				int x = i + dx[k], y = j + dy[k];
				if (x < 0 || x >= n || y < 0 || y >= m || s[x][y] == '#') {
					continue;
				}

				dsu.merge(i * m + j, x * m + y);
			}
		}
	}

	if (a.empty()) {
		std::cout << 0 << "\n";
		return;
	}

	{
		auto get = [&](const std::pair<int, int> & a) -> int {
			return a.first * m + a.second;
		};
		std::vector<int> cnta(n * m), cntb(n * m);
		for (auto & it : a) {
			++ cnta[dsu.find(get(it))];
		}

		for (auto & it : b) {
			++ cntb[dsu.find(get(it))];
		}

		for (int i = 0; i < n * m; ++ i) {
			if (cnta[i] != cntb[i]) {
				std::cout << -1 << "\n";
				return;
			}
		}
	}

	std::vector<std::tuple<int, int, char>> ans;
	auto work = [&](int sx, int sy) -> void {
		std::queue<std::pair<int, int>> q;
		memset(st, 0, sizeof st);
		pre[sx][sy] = {-1, -1};
		st[sx][sy] = 1;
		q.emplace(sx, sy);
		int tx, ty;
		while (q.size()) {
			auto [x, y] = q.front(); q.pop();
			if (a.count({x, y})) {
				tx = x, ty = y;
				break;
			}
			for (int i = 0; i < 4; ++ i) {
				int nx = x + dx[i], ny = y + dy[i];
				if (nx < 0 || nx >= n || ny < 0 || ny >= m || s[nx][ny] == '#' || st[nx][ny]) {
					continue;
				}
				pre[nx][ny] = {x, y};
				st[nx][ny] = 1;
				q.emplace(nx, ny);
			}
		}

		std::vector<std::pair<int, int>> path;
		a.erase({tx, ty});
		while (tx != sx || ty != sy) {
			path.emplace_back(tx, ty);
			auto & [nx, ny] = pre[tx][ty];
			tx = nx;
			ty = ny;
		}
		path.emplace_back(sx, sy);

		auto getop = [&](const std::pair<int, int> & a, const std::pair<int, int> & b) -> char {
			int x = a.first - b.first, y = a.second - b.second;
			if (x == -1 && y == 0) {
				return 'D';
			} else if (x == 1 && y == 0) {
				return 'U';
			} else if (x == 0 && y == -1) {
				return 'R';
			} else {
				return 'L';
			}
		};

		for (int i = 0; i + 1 < path.size(); ++ i) {
			if (c.count(path[i + 1])) {
				int j = i + 1;
				while (c.count(path[j + 1])) {
					++ j;
				}
				for (int k = j; k >= i; -- k) {
					ans.emplace_back(path[k].first, path[k].second, getop(path[k], path[k + 1]));
				}
				i = j;
			} else {
				ans.emplace_back(path[i].first, path[i].second, getop(path[i], path[i + 1]));
			}
		}

		c.emplace(sx, sy);
		b.erase({sx, sy});
	};

	while (b.size()) {
		auto & [x, y] = *b.begin();
		work(x, y);
	}
	std::cout << ans.size() << "\n";
	for (auto & [x, y, op] : ans) {
		std::cout << x + 1 << " " << y + 1 << " " << op << "\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

L. Ladder Challenge

题意:给你一个长度为\(n\)的数组,有\(Q\)次询问,每次给你初始值\(x\),你需要从小到大顺序一个一个数挑战,如果\(a_i\)小于等于\(x\)则跳到选一个数,否则一直使得\(x\)加一,\(a_i\)减一,直到\(x\)大于等于\(a_i\),目的是成为第\(y\)大的数。求需要操作多少次。

赛后补题。
思路来自\(jiangly\)代码。
\(x\)\(a_i\)操作后会变成\(\lceil \frac{x+a_i}{2} \rceil\),那么因为\(a_i, x \leq 1e9\),则最多\(30\)\(x\)的贡献就变的可有可无。也就是不同的\(x\)\(i\)出发,经过三十个数后相差最多为\(1\)。那么我们可以把询问离线,用\(map\)存所有不同的数,然后模拟。(这个做法实在是太简单了,只能说哥哥太强了)。
代码也是参考了\(jiangly\)的。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

struct DSU {
	std::vector<int> fa, cnt;
	DSU();
	DSU(int n) {
		init(n);
	}

	void init(int n) {
		fa.assign(n + 1, 0);
		cnt.assign(n + 1, 1);
		std::ranges::iota(fa, 0);
	}

	int find(int x) {
		return x == fa[x] ? x : fa[x] = find(fa[x]);
	}

	bool merge(int x, int y) {
		int u = find(x), v = find(y);
		if (u == v) {
			return false;
		}

		fa[v] = u;
		cnt[u] += cnt[v];
		return true;
	}

	bool same(int u, int v) {
		return find(u) == find(v);
	}

	int size(int u) {
		return cnt[find(u)];
	}
};

void solve() {
	int n, q;
	std::cin >> n >> q;
	std::vector<int> a(n); 
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	std::vector<std::vector<std::pair<int, int>>> add(n), del(n);
	for (int i = 0; i < q; ++ i) {
		int x, y;
		std::cin >> x >> y;
		int l = std::ranges::upper_bound(a, x) - a.begin();
		int r = n - y;
		if (l > r) {
			continue;
		}

		add[l].emplace_back(x, i);
		del[r].emplace_back(x, i);
	}

	std::vector<int> val(q), ans(q);
	DSU dsu(q);
	std::map<int, int> f;
	for (int i = 0; i < n; ++ i) {
		for (auto & [x, id] : add[i]) {
			if (f.contains(x)) {
				dsu.merge(f[x], id);
			} else {
				f[x] = id;
				val[id] = x;
			}
		}

		std::map<int, int> nf;
		for (auto & [x, id] : f) {
			int nx = (x + a[i] + 1) / 2;
			if (nf.contains(nx)) {
				dsu.merge(nf[nx], id);
			} else {
				nf[nx] = id;
				val[id] = nx;
			}
		}

		f = nf;
		for (auto & [x, id] : del[i]) {
			ans[id] = val[dsu.find(id)] - x;
		}
	}

	for (int i = 0; i < q; ++ i) {
		std::cout << ans[i] << "\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
posted @ 2025-07-25 01:21  maburb  阅读(492)  评论(15)    收藏  举报