Codeforces Global Round 13

第一次打Codeforces Global Round系列的比赛,感觉比赛时间太长了,时间太晚了,2个小时就溜了。。

A. K-th Largest Value

 A题链接

本来以为这个需要使用 快排扩展,求解第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

B题链接 

行数为 n((nleq100)), 列数为(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
", res);
    }

    return 0;
}
		

C. Pekora and Trampoline

C题链接

可以通过 (O(N^2))的算法进行解决。

首先,对于一个 trampoline ,当他 strength 减到 1之前,它能跳跃到其他的pos是固定的,长度减到一之后,就到 (pos + 1)位置。下面我们采取以下办法:

  • 对于 trampoline ,我们从 1 ~ n 进行循环
    • 设当前标号为 cur,他的初始强度为 (S_i),我们可以统计标号 1~(cur-1) 经过 i 的次数为 (B_i)
    • 倘若 (B_ileq S_i),那么需要以 i 为起点还需要的次数为 (S_i - 1 - B_i),将他累加到答案中
    • 否则,不需要单独以 i trampoline 作为起点
    • 下面我们需要更新 cur ~ N 号 蹦蹦床的 (B_i),即从之前节点经过的次数
      • (B_ileq S_i-1),对 cur + 2 ~ min(cur + S_i, n)进行更新,(B_i += 1)
      • (B_igeq 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

D题链接

通过查看数据范围,感觉 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
", x, y);
        x += sx, sx = 0;
        y += sy, sy = 0;	// sy 其实没啥用
        // printf("I:%d, %d
", 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;
}

原文地址:https://www.cnblogs.com/lucky-light/p/14462439.html