Codeforces Global Round 13
第一次打Codeforces Global Round系列的比赛,感觉比赛时间太长了,时间太晚了,2个小时就溜了。。
A. K-th Largest Value
本来以为这个需要使用 快排扩展,求解第K个数字,细看一下题目,因为数据非 0,即 1
可以直接统计 0, 1数量进行求解。
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int a[N];
int num[2];
int n, q;
int main()
{
cin >> n >> q;
num[0] = num[1] = 0;
for (int i = 1; i <= n; i ++ )
scanf("%d", &a[i]),
num[a[i]] ++;
while (q -- ) {
static int x, y;
scanf("%d %d", &x, &y);
if (x == 1) {
num[a[y]] --;
a[y] = 1 - a[y];
num[a[y]] ++;
} else {
if (num[1] >= y)
puts("1");
else
puts("0");
}
}
return 0;
}
B. Minimal Cost
行数为 n(\(n\leq100\)), 列数为\(10^6+2\)
因为 each row 有且仅有一个 obstacle,首先我们考虑什么时候才会堵住,因为列数太多,因此我们只能 竖向堵住,具体操作如图所示:
或者是倾斜的前进,总之,不能够让 S 可以从左侧到 右侧。因为一旦过去,就可以到 \(col=10^6+1\)进而移动到 终点 E 。
对于\(row=cur\) 障碍物所在位置为\(x\),倘若想要堵住,那么\(row=cur+1\)位置就需要变成\(x 或 x + 1 或 x - 1\).
划分清楚如何堵住之后,考虑一下 cost 代价的最小值
-
未堵住, \(cost = 0\)
-
倘若 \(a_1=a_2=...=a_n\),那么移动的最小代价即为 \(v + min(u, v)\)横向移动一次,在进行一次横向移动,或者是竖向移动。
-
否则, \(cost=min(u, v)\),因为对于任何一种存在斜对角的方案,可以通过横向移动,也可以通过竖向移动产生路径。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 110;
int a[N];
int num[2];
int n;
LL u, v;
int main()
{
int t; cin >> t;
LL res = 0;
while (t -- ) {
scanf("%d%lld%lld", &n, &u, &v);
for (int i = 1; i <= n; i ++ )
scanf("%d", &a[i]);
res = 0;
bool flag = false;
bool allSame = true;
for (int i = 2; i <= n; i ++ ) {
if (a[i] != a[i - 1])
allSame = false;
if (a[i] == a[i - 1] || a[i] == a[i-1] + 1 || a[i] == a[i-1] - 1)
continue;
else {
flag = true;
break;
}
}
if (!flag) { // 不能直接过去
if (allSame) {
res = v + min(u, v);
} else {
res = min(u, v);
}
} else {
res = 0;
}
printf("%lld\n", res);
}
return 0;
}
C. Pekora and Trampoline
可以通过 \(O(N^2)\)的算法进行解决。
首先,对于一个 trampoline ,当他 strength 减到 1之前,它能跳跃到其他的pos是固定的,长度减到一之后,就到 \(pos + 1\)位置。下面我们采取以下办法:
- 对于 trampoline ,我们从 1 ~ n 进行循环
- 设当前标号为 cur,他的初始强度为 \(S_i\),我们可以统计标号 1~(cur-1) 经过 i 的次数为 \(B_i\)
- 倘若 \(B_i\leq S_i\),那么需要以 i 为起点还需要的次数为 \(S_i - 1 - B_i\),将他累加到答案中
- 否则,不需要单独以 i trampoline 作为起点
- 下面我们需要更新 cur ~ N 号 蹦蹦床的 \(B_i\),即从之前节点经过的次数
- \(B_i\leq S_i-1\),对 cur + 2 ~ min(cur + S_i, n)进行更新,\(B_i += 1\)
- \(B_i\geq S_i-1\),对 cur + 2 ~ min(cur + S_i, n)进行更新,\(B_i += 1\),, 同时\(B_{cur+1} +=B_i-(S_i-1)\)
上面的算法,主要是求出了以每个节点作为最少起点次数的数量,然后累加求和得到了最小次数。
代码如下
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5010;
int a[N];
int b[N];
int n;
int main()
{
int t; cin >> t;
while (t -- ) {
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) {
scanf("%d", &a[i]);
}
memset(b, 0, sizeof b);
LL res = 0;
for (int i = 1; i <= n; i ++ ) {
if ((a[i] - 1) >= b[i]) { // a[i] - 1 是本身所需要的次数
res += (a[i] - 1) - b[i];
for (int j = 2; j <= a[i]; j ++ ) {
if (j + i < N) { // 跳跃的位置
b[i + j] ++;
} else {
break;
}
}
} else {
for (int j = 2; j <= a[i]; j ++ ) {
if (j + i < N) { // 跳跃的位置
b[i + j] ++;
} else {
break;
}
}
b[i + 1] += b[i] - (a[i] - 1);
}
}
cout << res << endl;
}
return 0;
}
D. Zookeeper and The Infinite Zoo
通过查看数据范围,感觉 DP打表啥的都不行,数据量太大了,因此只能按照原理从二进制01串入手。
-
首先,若\(u = v\),肯定可以到达
-
其次,若\(u>v\),那么肯定无法到达,因为只有低下标向高下标才存在路径。
-
最后,我们讨论当 \(u < v\),如何才可以到达
主要就是将从低位到高位,不断向目标串看齐,向上传递
不过当,当前位 = 1,低位向高位也传递了 1, 而目标是0,
此时有两种选择
- 两个 1 直接相加,向高位传递一个1
- 两个 1 各自向高位传递一个 1
因为两种方法都是可选了,而且我们也不想dfs,可以直接 传递一个 1, 然后加一个可选量 1,可用可不用即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5010;
// u <= v
bool Check(LL u, LL v) {
if (u > v) return false;
if (u == v) return true;
int x, y, sx = 0, sy = 0;
LL maxHave = 0;
for (int i = 0; i <= 34; i ++ ) {
x = ((u >> i) & 1);
y = ((v >> i) & 1);
// printf("I:%d, %d\n", x, y);
x += sx, sx = 0;
y += sy, sy = 0; // sy 其实没啥用
// printf("I:%d, %d\n", x, y);
if (x == y)
continue;
else if (x < y) { // y = 1, x = 0
if (maxHave == 0)
return false;
else {
maxHave -= 1;
}
}
else { // x > y, (1, 0), (2, 0), (2, 1)
sx = 1;
if (x == 2 && y == 0) // 可选值增加
maxHave += 1;
}
}
return true;
}
int main()
{
int t; cin >> t;
LL u, v;
while (t -- ) {
scanf("%lld%lld", &u, &v);
// if (u > v) swap(u, v);
if (Check(u, v))
puts("YES");
else
puts("NO");
}
return 0;
}