2025牛客暑期多校训练营3


A. Ad-hoc Newbie

题意:给定一个\(f\)数组,使得\(f[i] \leq i\)。你要构造一个矩阵\(a\),使得\(mex(a[1][i], a[2][i], ... a[n][i]) = mex(a[i][1], .. a[i][n]) = f[i]\)

可以构造\(ans[i][i] = 1\),然后\(ans[i][j] = ans[j][i] = i + 1\)。一个这样的矩阵。

\[\begin{matrix} 1&2&2&2&2&...&2\\ 2&1&3&3&3&...&3\\ 2&3&1&4&4&..&4\\ 2&3&4&1&5&...&5\\ &...&&&&&n\\ 2&3&4&5&...&n&1 \end{matrix}\]

然后,把所有\(ans[i][j] = f[i]\)\(ans[i][j] = f[j]\)的位置变成\(0\)
因为\(f[i] \leq i\),除了\(ans[i][i]\)\(1\)外,\(ans[i][j]\)肯定大于\(f[i]\)\(f[j]\)。而\(ans[i][i]\)肯定只会因为\(f[i]\)而变成\(0\),不会影响其它列,而这些大于\(f[i]\)的位置变成\(0\)也不会影响这一行列的\(mex\)

点击查看代码
#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];
	}

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

	for (int i = 0; i < n; ++ i) {
		for (int j = i + 1; j < n; ++ j) {
			ans[i][j] = ans[j][i] = i + 2;
		}
	}

	for (int i = 0; i < n; ++ i) {
		for (int j = 0; j < n; ++ j) {
			if (ans[i][j] == a[i] || ans[i][j] == a[j]) {
				ans[i][j] = 0;
			}
		}
	}

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

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

B. Bitwise Puzzle

题意:三个数\(a, b, c\),有四种操作,求\(64\)次操作内把\(a, b\)都变成\(c\)

如果\(b\)的最高位小于\(a\)的最高位,则\(b=b\oplus a\)
然后设\(b\)最高位为\(p\),那么我们可以利用这一位,把\(a\)的高\(30 - p\)位变得和\(c\)相同。也就是拿\(a\)的第\(p\)位和依次\(c\)的最高位比较,看要不要异或\(b\),然后每次把\(a\)左移。
此时再把低\(p\)位变的和\(c\)一样,只需要\(b\)不断除二,把最高位一步步拉低,就可以实现。最后\(b\)除成了\(0\),异或上\(a\)就变的和\(a\)一样了,此时\(a\)\(c\)一样,就构造了一组解。
\(a, b\)的乘\(2\)\(2\)操作总共\(31\)次,异或操作总共\(31\)次,一开始\(b\)\(a\)异或一次,最后又和\(a\)异或一次,总共是\(31 +31 + 1 + 1= 64\)次。

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

using i64 = long long;

void solve() {
	i64 a, b, c;
	std::cin >> a >> b >> c;
	if (a == 0 && b == 0) {
		if (c != 0) {
			std::cout << -1 << "\n";
			return;
		}
		std::cout << 0 << "\n";
		std::cout << "\n";
		return;
	}

	std::vector<int> ans;
	if (std::__lg(b) < std::__lg(a)) {
		ans.push_back(4);
		b ^= a;	
	}

	int p = 30;
	for (int i = 30; i >= 0; -- i) {
		if (b >> i & 1) {
			p = i;
			break;
		}
	}


	int j = 30;
	for (int i = 0; i < 30 - p; ++ i, -- j) {
		if ((a >> p & 1) != (c >> j & 1)) {
			ans.push_back(3);
			a ^= b;
		}

		ans.push_back(1);	
		a <<= 1;
	}

	for (int i = p; i >= 0; -- i, -- j) {
		if ((a >> i & 1) != (c >> j & 1)) {
			ans.push_back(3);
			a ^= b;
		}

		ans.push_back(2);
		b >>= 1;
	}

	ans.push_back(4);
	b ^= a;

	std::cout << ans.size() << "\n";
	for (auto & x : ans) {
		std::cout << x << " " ;
	}
	std::cout << "\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. Distant Control

题意:给你一个长度为\(n\)\(01\)数组和一个\(a\),每次可以把连续\(a\)\(1\)变成\(0\),或者把连续\(a+1\)\(0\)变成\(1\)。求最多有多少个\(1\)

如果我们可以进行第一个操作,肯定是操作一段旁边是\(0\)的,就得到大于等于\(a+1\)的连续个\(0\),就可以进行\(2\)操作,\(1\)的个数多\(1\),如此重复就可以全部变成\(1\)。同理如果可以进行\(2\)操作,那么我们可以进行\(1\)操作,就可以全部变成\(1\)。于是只要有连续\(a\)\(1\)或者连续\(a+1\)\(0\)就一定是\(n\),否则就是\(1\)的个数。

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

using i64 = long long;

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

		if (j - i + 1 >= m + (s[i] == '0')) {
			std::cout << n << "\n";
			return;
		}
		i = j;
	}

	std::cout << std::ranges::count(s.begin(), s.end(), '1') << "\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. Equal

题意:给你一个数组,每次选两个数除掉它们的一个共同因子,或者让他们同时乘上一个数。求能不能把所有数变成一样的。

注意到三个数\(x, y, z\),可以先\(x=xz, y=yz\),然后\(xz=xyz, z=yz\),再\(yz=xyz, yz=xyz\)。这样就变成相同的了,然后每次可以拿出其中一个数和另外两个数操作,得到\(xyz, xyz, xyzde, xyzde, xyzde\),此时剩下的两个\(xyz\)都乘上\(de\)就可以满足条件。然后归纳可得,奇数个一定可以变成一样的。
再考虑偶数个,考虑把所有质因子都拿出来一对一对除掉,那么对于每个质数,问题变成有\(n\)个数,每个数有\(a_i\)\(p\),每次选两个不同位置减一。能不能把数组都变成\(0\),这就是经典问题了,记\(sum\)\(p\)出现的总次数,\(max\)\(p\)在一个数上出现的最大次数,那么如果\(max > \lfloor \frac{sum}{2} \rfloor\)\(sum\)是奇数则不能满足,否则一定能把\(p\)消除干净。这样我们得到了一堆\(1\),和有若干个质因子的数,且这些质因子其它数是没有的。然后发现三个\(1\)可以拼出来\(p^{2k}\),那么如果剩下的质因子个数不是偶数个,无解。

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

using i64 = long long;

std::vector<int> minp, primes;

void sieve(int n) {
    minp.assign(n + 1, 0);
    primes.clear();
    
    for (int i = 2; i <= n; i++) {
        if (minp[i] == 0) {
            minp[i] = i;
            primes.push_back(i);
        }
        
        for (auto p : primes) {
            if (i * p > n) {
                break;
            }
            minp[i * p] = p;
            if (p == minp[i]) {
                break;
            }
        }
    }
}


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

	if (n & 1) {
		std::cout << "YES\n";
		return;
	}
	
	std::map<int, int> sum, max;
	for (int i = 0; i < n; ++ i) {
		int x = a[i];
		while (x > 1) {
			int p = minp[x];
			int cnt = 0;
			while (x % p == 0) {
				x /= p;
				++ cnt;
			}

			sum[p] += cnt;	
			max[p] = std::max(max[p], cnt);
		}
	}

	bool has = false;
	for (auto & [p, s] : sum) {
		if (max[p] > sum[p] / 2) {
			if ((max[p] - (sum[p] - max[p])) % 2) {
				std::cout << "NO\n";
				return;
			}
			has = true;
		} else {
			if (sum[p] % 2) {
				std::cout << "NO\n";
				return;
			}
		}
	}


	if (!has || n > 2) {
		std::cout << "YES\n";
	} else {
		std::cout << "NO\n";
	}
}

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

F. Flower

题意:每次拿\(a\)个再拿\(b\)个,求使得第\(n\)个在\(a\)个里最少删几个数。

先求得\(n\)本来是在\(a\)个里还是\(b\)个,直接对\(a+b\)取模就行。
如果\(n\leq a\),则需要删前面\(n\)个数,否则不用删。

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

using i64 = long long;

void solve() {
	int n, a, b;
	std::cin >> n >> a >> b;
	if (n <= a) {
		std::cout << "Sayonara\n";
		return;
	}

	n %= (a + b);
	if (n == 0) {
		n = a + b;
	}

	if (n > a) {
		std::cout << 0 << "\n";
	} else {
		std::cout << 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;
}

H. Head out to the Target

暴力模拟发现这个可以到达的点一定是根所在的一个联通块。那么对于每个\((u, l, r)\),找可以得到的最近祖先,然后暴力模拟。
具体可以用倍增求的祖先,然后计算和祖先的距离,如果小于等于\(r-l\)直接就可以到达,否则跳到祖先最深可以到的点,一步一步往上爬模拟就行。

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

using i64 = long long;

void solve() {
	int n, k;
	std::cin >> n >> k;
	std::vector<std::vector<int>> adj(n);
	for (int i = 1; i < n; ++ i) {
		int j;
		std::cin >> j;
		-- j;
		adj[j].push_back(i);
	}

	std::vector<std::tuple<int, int, int>> a(k);
	for (int i = 0; i < k; ++ i) {
		int u, l, r;
		std::cin >> u >> l >> r;
		-- u;
		a[i] = {u, l, r};
	}

	int lg = std::__lg(n) + 1;
	std::vector fa(n, std::vector<int>(lg + 1));
	std::vector<int> dep(n);
	auto dfs = [&](auto & self, int u) -> void {
		for (auto & v : adj[u]) {
			dep[v] = dep[u] + 1;
			fa[v][0] = u;
			for (int i = 1; i <= lg; ++ i) {
				fa[v][i] = fa[fa[v][i - 1]][i - 1];
			}
			self(self, v);
		}
	};

	dfs(dfs, 0);
	std::vector<int> st(n, 0);
	st[0] = 1;
	for (auto & [v, l, r] : a) {
		if (st[v]) {
			std::cout << l << "\n";
			return;
		}
		int u = v;
		for (int i = lg; i >= 0; -- i) {
			if (!st[fa[u][i]]) {
				u = fa[u][i];
			}
		}

		int d = dep[v] - dep[u];
		if (r - l >= d) {
			std::cout << l + d << "\n";
			return;
		}

		d -= r - l;
		u = v;
		for (int i= lg; i >= 0; -- i) {
			if (dep[u] - dep[fa[u][i]] <= d) {
				d -= dep[u] - dep[fa[u][i]];
				u = fa[u][i];
			}
		}
		
		while (!st[u]) {
			st[u] = 1;
			u = fa[u][0];
		}
	}

	std::cout << -1 << "\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;
}

J. Jetton

一开始一直在观察性质,不过没什么用。。。
发现小的数每次都会乘二,猜测有解不会进行很多轮,直接暴力模拟1000次就过了。

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

using i64 = long long;

void solve() {
	i64 x, y;
	std::cin >> x >> y;
	if (x == y) {
		std::cout << 1 << "\n";
		return;
	}
	
	if ((x + y) % 4) {
		std::cout << -1 << "\n";
		return;
	}

	if ((x + y) / 4 % 2) {
		if (x % 2 == 0 && y % 2 == 0) {
			std::cout << -1 << "\n";
			return;
		}
	}


	int ans = 0;
	int cnt = 0;
	while (x != 0 && y != 0) {
		if ( ++ cnt > 1000) {
			std::cout << -1 << "\n";
			return;
		}
		if (x > y) {
			std::swap(x, y);
		}
		++ ans;	
		y -= x;
		x += x;
	}

	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;
}
posted @ 2025-07-23 11:02  maburb  阅读(187)  评论(0)    收藏  举报