前言:
- 牛客竞赛bilibili官方题解更为详细
- 点击此处进入bilibili牛客竞赛
- 本题的D-F题代码均摘自于牛客官方题解
A - 相遇
思路:
- 枚举即可
以下是代码部分
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
if(a == b) cout << "p";
else if(a == 1 && b == 2) cout << 'a';
else if(a == 2 && b == 3) cout << "a";
else if(a == 3 && b == 1) cout << 'a';
else cout << 'b';
return 0;
}
B-宝石
思路:
- 总共就两种最短路的方法
- 只通过路径长度为
A
的路拿取宝石,路程总和为11*A
- 通过1条长度为
A
,5条长度为B
的途径拿取宝石 路程总和为:A+5*B
。
- 只通过路径长度为
- 选取路程最短的路,输出。
以下是代码部分
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
cout << min(11 * a, 5 * b + a);
return 0;
}
C - 相助
思路:
- 因为此题
a[i]
只有0
和1
两种元素,所以分类讨论即可- 当开头元素等于末尾元素时: 操作数为
1
- 当元素数量
n=1
时:不可操作,输出:-1
- 当第
i
位元素和开头元素相等,并且i+1
位元素和结尾元素相等时,操作数为:2
。 - 都不符合的情况,为不可操作,输出:
-1
- 比如:
0100001
符合情况4
- 比如:
- 当开头元素等于末尾元素时: 操作数为
以下是代码部分
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int a[N];
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i ++)
cin >> a[i];
//取头位置元素, 取尾位置元素
int head = a[1], tail = a[n];
//情况2
if(n == 1)
return cout << "-1\n", 0;
//情况1
if(head == tail)
return cout << "1\n", 0;
//遍历判断情况3是否成立
for(int i = 2; i < n - 1; i ++)
if(head == a[i] && tail == a[i + 1])
{
cout << "2\n";
return 0;
}
//情况4
cout << "-1\n";
return 0;
}
D - 异或炸弹(easy)
思路:
- 前置知识:差分数组
- 在每次投放炸弹后,差分炸弹在每一行可以达到的范围。
- 最后遍历得出为
1
的个数
差分数组简要介绍:
- 在数组
a[]
中 - 如果要使区间
l-r
的每个元素+1
- 可以使
a[l] + 1
使a[r + 1] - 1
- 在处理后,通过前缀和的操作,就可以使区间
a[l] - a[r]
的每个元素+1
并且其他区间不受影响。
以下是代码部分,代码参考来源——FriedChicken
#include<bits/stdc++.h>
using namespace std;
int mp[6010][6010];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
int x, y, r;
while(m --)
{
//输入炸弹的坐标以爆炸范围
cin >> x >> y >> r;
//遍历x方向上的曼哈顿范围
for(int i = x - r; i <= x + r; i ++)
{
//注意不可越界
if(i < 1 || i > n) continue;
//求每一行的曼哈顿范围
int l = r - abs(x - i);
//差分处理
//注意不可以越界
mp[i][max(1, y - l)] ^= 1;
mp[i][min(n + 1, y + l + 1)] ^= 1;
}
}
int ans = 0;
//遍历地图,计数
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
{
//前缀预处理
mp[i][j] ^= mp[i][j - 1];
//如果为mp[i][j]上的位置为1,则ans ++
ans += mp[i][j] & 1;
}
cout << ans << '\n';
return 0;
}
E - 相依
思路:
- 动态规划
dp[i]
:dp[消除1-i项]所需最少操作次数mn[i]
:mn[值的大小为i的元素]被消除所需的最小次数- 可得递推公式:
dp[i] = mn[a[i]] + 1
消除1-i
的元素,为消除a[i]
的操作数+1
(因为之前mn[a[i]]是未匹配过的)mn[a[i]] = min(mn[a[i]], dp[i - 1])
在第i
位的a[i]
未匹配时,mn[a[i]]
的状态转移方式
以下是代码部分,代码参考来源——FriedChicken
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int a[N], dp[N], mn[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
// 初始化为无穷
for(int i = 0; i < N; i ++) mn[i] = 1e9;
//输入
for(int i = 1; i <= n; i ++) cin >> a[i];
//递推公式
for(int i = 1; i <= n; i ++)
{
dp[i] = mn[a[i]] + 1;
mn[a[i]] = min(mn[a[i]], dp[i - 1]);
}
//如果无法操作
if(dp[n] >= 1e6) cout << "-1\n";
//可以操作的时候,输出
else cout << dp[n] << '\n';
return 0;
}
F - 异或炸弹(hard)
前置知识:
- 切比雪夫距离和曼哈顿距离的互相转化
- 点此跳转详解链接
- 二维前缀和,二维数组差分
思路:
- 通过曼哈顿距离转切比雪夫距离,来方便二维前缀和,二维差分的处理
- 注意,本题通过数组来模拟坐标,所以要对点进行Y轴上的偏移,防止为负数,导致越界
- 处理好后,还要切比雪夫距离转曼哈顿距离,把多算的点排除掉
曼哈顿距离转切比雪夫
(
x
+
y
,
x
−
y
)
(x+y, x-y)
(x+y,x−y)
切比雪夫转曼哈顿距离
(
x
+
y
/
2
,
x
−
y
/
2
)
(x+y/2, x-y/2)
(x+y/2,x−y/2)
以下是代码部分
#include<bits/stdc++.h>
using namespace std;
const int N = 6e3 + 10;
int f[N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for(int i = 1; i <= m; i ++)
{
int x, y, r;
cin >> x >> y >> r;
// 曼哈顿距离转换成切比雪夫距离
// +3000 为防止出现负数,放于坐标系f[][]中
int nx = x + y, ny = x - y + 3000;
//转换成切比雪夫距离后,便于差分
//二维差分
f[max(1, nx - r)][max(1, ny - r)] ^= 1;
f[min(6000, nx + r + 1)][max(1, ny-r)] ^= 1;
f[max(1, nx-r)][min(6000, ny + r + 1)] ^= 1;
f[min(6000, nx + r + 1)][min(6000, ny + r + 1)] ^= 1;
}
int ans = 0;
for(int i = 1; i <= 6000; i ++)
for(int j = 1; j <= 6000; j ++)
{
//二维前缀和
f[i][j] = f[i-1][j]^f[i-1][j-1]^f[i][j-1]^f[i][j];
//曼哈顿距离转换成切比雪夫距离
int x = (i + j - 3000) / 2, y = (i - j + 3000) / 2;
//判断值是否为1,(i+j)和(i-j)是否是偶数, 是否越界
if(f[i][j] && (i&1) == (j&1) && x > 0 && x <= n && y > 0 && y <= n)
ans ++;
}
cout << ans << '\n';
return 0;
}