2025牛客暑期多校训练营6


B. Base Conversion Master

题意:给你\(n, y, m\),和\(n\)个数组,你选择一个进制\(s \in [2, m]\),依次对\(n\)个数组进行转换操作,操作一个数组后把进制改为这个数组的结果。求最终\(s=y\)的区间。

显然大的进制得到的结果一定大于等于小的进制。
那么直接二分就行了,这个题确实很简单吧。。。榜太歪了。

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

using i64 = long long;

void solve() {
	int n, y, m;
	std::cin >> n >> y >> m;
	std::vector<std::vector<int>> a(n);
	for (int i = 0; i < n; ++ i) {
		int l;
		std::cin >> l;
		a[i].resize(l);
		for (int j = 0; j < l; ++ j) {
			std::cin >> a[i][j];
		}
	}

	const i64 inf = 1e9;

	auto check = [&](int p) -> i64 {
		for (auto & b : a) {
			i64 res = 0;
			for (auto & x : b) {
				if (x >= p) {
					return 0;
				}
				res = std::min(inf, res * p + x);
			}
			
			p = res;
		}
		return p;
	};

	int l = 1, r = m;
	while (l < r) {
		int mid = l + r >> 1;
		if (check(mid) >= y) {
			r = mid;
		} else {
			l = mid + 1;
		}
	}

	int L = l;
	l = 1, r = m;
	while (l < r) {
		int mid = l + r + 1 >> 1;
		if (check(mid) <= y) {
			l = mid;
		} else {
			r = mid - 1;
		}
	}

	if (check(L) != y) {
		std::cout << -1 << " " << -1 << "\n";
		return;
	}


	int R = l;
	std::cout << L << " " << R << "\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;
}

C. Stack

题意:对一个排列进行单调栈操作,最终价值就是栈的大小的立方。求所以排列的价值和。

考虑已经放好前\([1, i - 1]\)个数,考虑第\(i\)个数怎么放,除了放在最后面的情况,插在最前面或者两个数中间都不影响单调栈的大小,也就是贡献\(i-1\)\([1, i-1]\)排列的值。再考虑放在最后的情况,发现\([1, i - 1]\)每个排列的价值都要加一,我们记\([1, i - 1]\)\((i-1)!\)个排列的价值依次为\(x_1, x_2, ..., x_{(i-1)!}\),我们知道\((x+1)^3 = x^3+3x^2 + 3x + 1\),也就是这些排列贡献的价值为\(x_1^3+3x_1^2 + 3x_1 + 1 + x_2^3+3x_2^2 + 3x_2 + 1...\)。发现既有三次幂又有偶数幂,无法直接计算。
考虑\(dp\),记\(f[i][j]\)为前\(i\)个数\(\sum x^j\)的和。那么可得\(f[i][0] = (i - 1) \times f[i - 1][0] + f[i - 1][0]\),也就是\(i \times f[i - 1][0]\)。同理得\(f[i][1] = i \times f[i - 1][1] + f[i][0]\)\(f[i][2] = i \times f[i][2] + 2\times f[i][1] + f[i][0]\)\(f[i][3] = i \times f[i-1][3] + 3\times f[i-1][2] + 3\times f[i-1][1] + f[i-1][0]\)
那么预处理,初始化为\(f[0][0] = 1\),然后\(f[n][3]\)\(n\)的答案。
代码省略取模类。

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

Z f[500010][4];

void init(int n) {
    f[0][0] = 1;
    for (int i = 1; i <= n; ++ i) {
        f[i][0] = i * f[i - 1][0];
        f[i][1] = i * f[i - 1][1] + f[i - 1][0];
        f[i][2] = i * f[i - 1][2] + 2 * f[i - 1][1] + f[i - 1][0];
        f[i][3] = i * f[i - 1][3] + 3 * f[i - 1][2] + 3 * f[i - 1][1] + f[i - 1][0];
    }
}

void solve() {
	int n;	
	std::cin >> n;
    std::cout << f[n][3] << "\n";
}

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

G. Turn around

题意:一个只包含\(RL\)的数组,如果一个时刻有\(RL\)这个子串,两个这两个字符会换位置,一直操作到没有\(RL\)这个子串为止。求操作多少次,并且多组查询,每次会修改一个位置。

最后一定是所有\(L\)在左边,\(R\)在右边。
那么如果我们求得每个\(L\)走到对应位置的时间,取最大值就能得到答案。
如果求第\(j\)\(L\)\(j\)的时间,我们需要记一个前缀\(cnt\),表示前缀\(R\)的数量,那么如果第\(j\)\(L\)\(i\)的位置,则这个\(L\)至少需要\(cnt_i\)的时间,而它可能被前面的\(L\)卡住,那么到达时间需要和前面的位置的时间加一取最大。这样可以求出不修改的答案,但如果修改,发现无法更新,因为前缀\(R\)的数量改变,需要一个一个改后面的值。
\(f_i\)为在\(i\)看来\([i, n]\)\(0\)的最小时间,也就是所有位置复位后都整体移到\(1\)的左边。那么对于一个\(i\),如果不管前面的\(L\)的话需要\(i\)的时间到\(0\),如果它后面多一个\(L\),那么对\(i\)来说\([i, n]\)这个后缀的时间需要加\(2\)。因为后面这个\(L\)要么等前面的一步,然后再走,要么与前面的相差大于等于\(2\),那么到\(0\)的时间自然也多\(2\)。于是我们给每个\(s_i = L\)的位置给\([1, i - 1]\)都加\(2\)。就可以求得每个位置计算出的最小时间,这些时间取\(max\)后减去\(L\)的数量就是答案。因为我们计算的到是\(0\)的时间,而其实从左到右第\(j\)\(L\)只需要到\(j\)这个位置。
这样之所以能求得正确答案,因为最大的\(f_i\)肯定能得到最后的\(L\)到达\(0\)的时间,且最后的\(L\)肯定比前面的要慢,所以直接用最大值减\(L\)个数就是最后一个\(L\)复位的答案。
那么修改的时候只需要讨论其对前缀的贡献的更改就行了。不过还要记录后缀\(L\)的个数,这样一个\(R\)变成\(L\)的时候才能知道自己需要加多少个\(2\),然后需要记录最左边的\(R\)的位置,前面已经复位的\(L\)不用管,查询最左边\(R\)的右边的\(L\)的值就行。
于是用线段树维护区间最大值,树状数组维护区间\(L\)的个数。

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

using i64 = long long;

template <class Info, class Tag>
struct LazySegmentTree {
	struct Node {
		int l, r;
		Info info;
		Tag tag;
	};

	std::vector<Node> tr;
	LazySegmentTree() {}
	LazySegmentTree(int n) {
		init(n);
	}

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

	void pushdown(Node & u, const Tag & tag) {
		u.info = u.info + tag;
		u.tag = u.tag + tag;
	}

	void pushdown(int u) {
		if (tr[u].tag.exist()) {
			pushdown(tr[u << 1], tr[u].tag);
			pushdown(tr[u << 1 | 1], tr[u].tag);
			tr[u].tag.clear();
		}
	}

	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) {
			tr[u].info.max = -1e9;
			return;
		}

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

	void modify(int l, int r, const Tag & tag, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			pushdown(tr[u], tag);
			return;
		}

		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if (l <= mid) {
			modify(l, r, tag, u << 1);
		}

		if (r > mid) {
			modify(l, r, tag, u << 1 | 1);
		}

		pushup(u);
	}

	void modify(int p, int v) {
		int u = 1;
		while (tr[u].l != tr[u].r) {
			pushdown(u);
			int mid = tr[u].l + tr[u].r >> 1;
			if (p <= mid) {
				u = u << 1;
			} else {
				u = u << 1 | 1;
			}
		}

		tr[u].info.max = v;

		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;
		}

		pushdown(u);
		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 max;
};

struct Tag {
	int add;
	bool exist() {
		return add != 0;
	}

	void clear() {
		add = 0;
	}
};

Info operator + (const Info & a, const Info & b) {
	Info res{};
	res.max = std::max(a.max, b.max);
	return res;
}

Info operator + (const Info & a, const Tag & b) {
	Info res{};
	res.max = a.max + b.add;
	return res;
}

Tag operator + (const Tag & a, const Tag & b) {
	Tag res{};
	res.add = a.add + b.add;
	return res;
}

template <class T>
struct Fenwick {
    int n;
    std::vector<T> tr;

    Fenwick(int _n) {
        init(_n);
    }

    void init(int _n) {
        n = _n;
        tr.assign(_n + 1, T{});
    }

    void add(int x, const T &v) {
        for (int i = x; i <= n; i += i & -i) {
            tr[i] = tr[i] + v;
        }
    }

    T query(int x) {
        T res{};
        for (int i = x; i; i -= i & -i) {
            res = res + tr[i];
        }
        return res;
    }

    T sum(int l, int r) {
        return query(r) - query(l - 1);
    }
};

void solve() {
	int n, q;
	std::cin >> n >> q;
	std::string s;
	std::cin >> s;
	const int inf = 1e9;
	LazySegmentTree<Info, Tag> tr(n + 1);
	std::set<int> p;
	int cnt = 0;
	Fenwick<int> tr1(n + 1);
	for (int i = 0; i < n; ++ i) {
		if (s[i] == 'L') {
			tr.modify(i, i + 1);
			tr1.add(i + 1, 1);
			if (i) {
				tr.modify(0, i - 1, Tag{2});
			}
			++ cnt;
		} else {
			tr.modify(i, -inf);
			p.insert(i);
		}
	}

	while (q -- ) {
		int x;
		std::cin >> x;
		-- x;
		if (s[x] == 'L') {
			tr.modify(x, -inf);
			if (x) {
				tr.modify(0, x - 1, Tag{-2});
			}
			-- cnt;
			p.insert(x);
			tr1.add(x + 1, -1);
			s[x] = 'R';
		} else {
			tr.modify(x, x + 1 + tr1.sum(x + 1, n) * 2);
			if (x) {
				tr.modify(0, x - 1, Tag{2});
			}
			++ cnt;
			p.erase(x);
			tr1.add(x + 1, 1);
			s[x] = 'L';
		}


		if (p.empty()) {
			std::cout << 0 << "\n";
		} else {
			std::cout << std::max(0, tr.query(*p.begin(), n - 1).max - cnt) << "\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;
}

K. Maximum GCD

题意:一个数组,可以进行一次操作,选择一个区间和一个数\(x\),给所有数加上\(x\)。求数组的\(gcd\)最大是多少。

如果不操作整个数组的话,显然\(gcd\)一定是\(a_1, a_n\)的因子。如果操作整个数组,记最终\(gcd\)\(d\),那么因为\(a_i + x \equiv a_j + x \pmod{d}\),则\(a_i - a_j \equiv 0 \pmod{d}\),这意味这\(d\)是一对\(a_i - a_j\)的因子。那么把这几个数的因子搞出来跑一遍看可不可行。

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

using i64 = long long;

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

	if (std::ranges::count(a, a[0]) == n) {
		std::cout << 0 << "\n";
		return;
	}

	std::vector<int> b;
	auto get = [&](int x) -> void {
		for (i64 i = 1; i * i <= x; ++ i) {
			if (x % i == 0) {
				b.push_back(i);
				if (x % (x / i) == 0) {
					b.push_back(x / i);
				}
			}
		}
	};

	get(a[0]);
	get(a[n - 1]);
	auto c = a;
	std::ranges::sort(c);
	int min = 2e5;
	for (int i = 0; i + 1 < n; ++ i) {
		if (c[i] != c[i + 1]) {
			min = std::min(min, c[i + 1] - c[i]);
		}
	}

	get(min);

	std::vector<int> pre(n + 1), suf(n + 2);
	for (int i = 0; i < n; ++ i) {
		pre[i + 1] = std::gcd(pre[i], a[i]);
	}

	for (int i = n - 1; i >= 0; -- i) {
		suf[i + 1] = std::gcd(suf[i + 2], a[i]);
	}

	std::ranges::sort(b);
	b.erase(std::unique(b.begin(), b.end()), b.end());
	std::ranges::reverse(b);
	int ans = pre[n];
	for (auto & d : b) {
		int l = 0, r = n;
		while (l < r) {
			int mid = l + r + 1 >> 1;
			if (pre[mid] % d) {
				r = mid - 1;
			} else {
				l = mid;
			}
		}

		int L = l;
		l = 1, r = n + 1;
		while (l < r) {
			int mid = l + r >> 1;
			if (suf[mid] % d) {
				l = mid + 1;
			} else {
				r = mid;
			}
		} 

		int R = l;
		std::set<int> s;
		for (int i = L + 1; i < R; ++ i) {
			s.insert(a[i - 1] % d);
		}

		if (s.size() == 1) {
			ans = d;
			break;
		}
	}


	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;
}

L. Minimum Parenthesis String

题意:构造一个括号序列,有\(m\)个要求:\([l_i, r_i]\)内至少一个左括号。要求字典序最小。

先构造字典序最小的括号序列,\(n\)个(加\(n\)个)。然后把要求按左端点从大到小排序,如果\([l_i, r_i]\)内没有左括号,则拿前半部分最后的左括号和\(l_i\)换位置。最后判断是不是合法的。

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

using i64 = long long;

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

	std::string s = std::string(n, '(') + std::string(n, ')');
	std::ranges::sort(a, std::greater<>());
	int last = 2 * n;
	int p = n - 1;
	for (auto & [l, r] : a) {
		-- l, -- r;
		if (l <= p) {
			break;
		}
		if (r < last) {
			if (p < 0) {
				std::cout << -1 << "\n";
				return;
			}
			std::swap(s[p], s[l]);
			last = l;
			-- p;
		}
	}

	int sum = 0;
	for (auto & c : s) {
		sum += c == '(' ? 1 : -1;
		if (sum < 0) {
			std::cout << -1 << "\n";
			return;
		}
	}
	std::cout << s << "\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-08-01 11:09  maburb  阅读(217)  评论(0)    收藏  举报