比赛链接:

https://ac.nowcoder.com/acm/contest/9925

B.Mine Sweeper II

题意:

给定两个扫雷矩阵,'X' 表示这个位置上是地雷,'.' 表示一个数字,会显示周围(3 * 3的矩阵)有几个地雷。
可以改变矩阵 B 的某个元素,将 'X' 变成 '.' 或者反过来,改变次数不超过 \(\lfloor \frac{n * m}{2} \rfloor\)。要求改变后,两个矩阵的数字之和相同。

思路:

将相邻的两个格子作为一组取出来,当它们不同的时候,就会对数字和产生 1 的贡献,所以整个矩阵取反之后,数字和是不变的。
所以对比 B 矩阵和 A 矩阵的差别,以及和 A 矩阵的反矩阵的差别,取一个 \(<= \lfloor \frac{n * m}{2} \rfloor\) 变过去就行。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n, m;
	cin >> n >> m;
	vector <string> a(n), b(n);
	for (int i = 0; i < n; i ++ )
		cin >> a[i];
	for (int i = 0; i < n; i ++ )
		cin >> b[i];
	int cnt = 0;
	for (int i = 0; i < n; i ++ )
		for (int j = 0; j < m; j ++ )
			cnt += (a[i][j] != b[i][j]);
	if (cnt > (n * m) / 2){
		for (int i = 0; i < n; i ++ )
			for (int j = 0; j < m; j ++ )
				a[i][j] = (a[i][j] == '.' ? 'X' : '.');
	}
	for (int i = 0; i < n; i ++ )
		cout << a[i] << "\n";
	return 0;
}

C.Sum of Log

题意:

\(T\) 组数据,每组给定 \(X\)\(Y\),求 \(\sum_{i = 0}^{X} \sum_{j = [i == 0]}^{Y} [i \& j = 0] \lfloor log_2(i + j) + 1 \rfloor\),答案对 1e9 + 7 取模。

思路:

\(\lfloor log_2(i + j) + 1 \rfloor\) 就是 \(i + j\) 的二进制长度。
如果将一个数按照二进制拆分开来,枚举每一位,当某一个组合枚举到最后,前导零还是存在(即每一位上都是 0,符合 \(i \& j = 0\) 的要求),那么这个数就是一个答案,所以可以通过数位 dp 去计算答案。最后的答案要减 1,因为 0 的情况不存在。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 35, mod = 1e9 + 7;
LL dp[N][2][2][2];
int a[N], b[N];
LL dfs(int pos, int limit1, int limit2, int zero){
	if (pos == 0) return 1;
	if (dp[pos][limit1][limit2][zero] != -1) return dp[pos][limit1][limit2][zero];
	int up1 = limit1 ? a[pos] : 1;
	int up2 = limit2 ? b[pos] : 1;
	LL ans = 0;
	for (int i = 0; i <= up1; i ++ ){
		for (int j = 0; j <= up2; j ++ ){
			if (i & j) continue;
			LL k = (zero && (i || j)) ? pos : 1;
			ans = (ans + dfs(pos - 1, limit1 && i == up1, limit2 && j == up2, zero && !i && !j) * k % mod) % mod;
		}
	}
	return dp[pos][limit1][limit2][zero] = ans;
}
void solve(){
	int x, y;
	cin >> x >> y;
	memset(a, 0, sizeof a);
	memset(b, 0, sizeof b);
	int len1 = 0, len2 = 0;
	while(x){
		a[ ++ len1] = x & 1;
		x >>= 1;
	}
	while(y){
		b[ ++ len2] = y & 1;
		y >>= 1;
	}
	memset(dp, -1, sizeof dp);
	cout << dfs(max(len1, len2), 1, 1, 1) - 1 << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

D.Walker

题意:

有一个线段,范围为 \([0, n]\),有两个人分别在 \(p_1, p_2\),速度分别为 \(v_1, v_2\),问它们把整个线段走完的最短时间。

思路:

按照每个人走的方向,有四种情况。
四个情况都有一个共同情况,一个人将整个线段遍历了,后续就不讲了。
右右,左边的人走左边的一部分即可返回了,即每个人遍历自己的一部分线段。
右左,两个人碰到之后就可以返回了,也是每个人都遍历自己的一部分线段,或者每个人直接走到头。
左右,也是每人遍历自己的一部分线段即可。
左左,右边的人走到左边的一部分就可以返回了,也是每个人遍历自己的线段。
总结以下,就三种情况。
1.一个人将整个线段走完。
2.两个人相对着走将线段走完。
3.选择一个区分点,将线段一分为二,每人走自己的部分。通过二分中间节点去求解。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
	double n, p1, v1, p2, v2;
	cin >> n >> p1 >> v1 >> p2 >> v2;
	if (p1 > p2){
		swap(p1, p2);
		swap(v1, v2);
	}
	double ans = max((n - p1) / v1, p2 / v2);
	ans = min(ans, min(n + p1, 2 * n - p1) / v1);
	ans = min(ans, min(n + p2, 2 * n - p2) / v2);
	double L = p1, R = p2;
	while (R - L > 1e-7){
		double mid = (L + R) / 2;
		double tL = min(p1 + mid, 2 * mid - p1) / v1;
		double tR = min(n + p2 - 2 * mid, 2 * n - p2 - mid) / v2;
		ans = min(ans, max(tL, tR));
		if (tL > tR) R = mid;
		else L = mid;
	}
	cout << fixed << setprecision(10) << ans << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

G.Fibonacci

题意:

\(f_i\) 表示斐波那契数列的第 \(i\) 个数。
\(g(x, y)\):当 \(x * y\) 是偶数时返回 1,否则返回 0。
\(\sum_{i = 1}{n} \sum{j = i + 1}{n} g(f_i, f_j)\)

思路:

可以发现斐波那契数列分布符合,奇数,奇数,偶数,奇数,奇数,偶数...
相乘为偶数,只有其中一个数为偶数的情况,对于 \(n\),总共有 \(\lfloor \frac{n}{3} \rfloor\) 个偶数,记为 \(k\),每个偶数对答案的贡献是 \(n - 1\),总共有 \(\frac{k * (k - 1)}{2}\) 个重复的。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL n;
	cin >> n;
	LL k = n / 3;
	cout << k * (n - 1) - k * (k - 1) / 2 << "\n";
	return 0;
}

M.Gitignore

题意:

给出 \(n\) 个文件的路径(可以隐藏),以及 \(m\) 个不能隐藏的文件路径。同一个文件夹中的文件可以隐藏在一起(在所有文件都可以隐藏的前提下),问最少通过几个文件夹可以将所有文件包括进来。

思路:

先记录不能隐藏的文件路径,然后假设所有文件都不能隐藏,每次遍历每个文件路径,如果路径上某个文件不能隐藏,那么这条路径就是不能删除的,否则就先记录下,如果是第一次出现,那么不能隐藏,否则都可以和第一个一起隐藏。

代码:

#include <bits/stdc++.h>
using namespace std;
void solve(){
	int n, m;
	cin >> n >> m;
	vector <string> a(n), b(m);
	for (int i = 0; i < n; i ++ )
		cin >> a[i];
	for (int i = 0; i < m; i ++ )
		cin >> b[i];
	map <string, int> Map;
	for (int i = 0; i < m; i ++ ){
		int len = b[i].size();
		string t = "";
		for (int j = 0; j < len; j ++ ){
			t += b[i][j];
			if (b[i][j] == '/'){
				Map[t] = 1;
			}
		}
	}
	int ans = n;
	for (int i = 0; i < n; i ++ ){
		int len = a[i].size();
		string t = "";
		for (int j = 0; j < len; j ++ ){
			t += a[i][j];
			if (a[i][j] == '/'){
				if (Map[t] == 0) Map[t] = 2;
				else if (Map[t] == 2){
					ans -- ;
					break;
				}
			}
		}
	}
	cout << ans << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}
posted on 2022-10-16 16:16  Hamine  阅读(136)  评论(0编辑  收藏  举报