比赛链接:
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;
}