Educational Codeforces Round 90 (Rated for Div. 2)

A - Donut Shops

void TestCase() {
    ll a, b, c;
    scanf("%lld%lld%lld", &a, &b, &c);
    if(a > c) {
        printf("-1 1
");
        return;
    } else if(a == c) {
        printf("-1 %lld
", b);
        return;
    } else {
        if(a * b <= c) {
            printf("1 -1
");
            return;
        } else {
            printf("1 %lld
", b);
            return;
        }
    }
}

B - 01 Game

容易注意到,只要'0'和'1'的其中一种还没用完,都是始终可以操作的。所以就是判断一下距离用完其中一种需要的步数的奇偶性。

char s[20005];
 
void TestCase() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    int cnt0 = 0, cnt1 = 0;
    for(int i = 1; i <= n; ++i) {
        if(s[i] == '0')
            ++cnt0;
        else
            ++cnt1;
    }
    if(min(cnt0, cnt1) % 2 == 1)
        puts("DA");
    else
        puts("NET");
    return;
}

C - Pluses and Minuses

题意:给一个串,串里面只有'+'和'-',执行下面的伪代码,求res的值。

res = 0
for init = 0 to inf
    cur = init
    ok = true
    for i = 1 to |s|
        res = res + 1
        if s[i] == '+'
            cur = cur + 1
        else
            cur = cur - 1
        if cur < 0
            ok = false
            break
    if ok
        break

题解:容易知道随着 init 的增大每次 break 会越来越晚, break 的时候就是第一次使得 init + prefix < 0 的位置,最后当 init 超过了最小的 prefix 就会保持 ok 为 true 然后结束程序。所以有个简单的想法就是每次二分找第一个使得 init + prefix < 0 的位置。

char s[1000005];
int prefix[1000005];
int prefixmin[1000005];
 
void TestCase() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for(int i = 1; i <= n; ++i) {
        prefix[i] = prefix[i - 1];
        if(s[i] == '+')
            ++prefix[i];
        else
            --prefix[i];
    }
    prefixmin[1] = prefix[1];
    for(int i = 2; i <= n; ++i)
        prefixmin[i] = min(prefixmin[i - 1], prefix[i]);
 
    ll res = 0;
    for(int init = 0;; ++init) {
        int L = 1, R = n, ans = -1;
        while(1) {
            int M = (L + R) >> 1;
            if(L == M) {
                if(prefixmin[L] + init < 0) {
                    ans = L;
                    break;
                } else if(prefixmin[R] + init < 0) {
                    ans = R;
                    break;
                } else
                    break;
            }
            if(prefixmin[M] + init < 0)
                R = M;
            else
                L = M + 1;
        }
        if(ans == -1) {
            res += n;
            break;
        } else
            res += ans;
    }
    printf("%lld
", res);
    return;
}

事实上也可以当每次 cur < 0 的时候(也就是 cur = -1 的时候)把 cur 重置为 0 并统计现在的贡献(其实就是当前的下标),因为下一次 init 增加了之后,到这个位置 cur 就会 cur = 0 ,所以可以这样转化。

D - Maximum Sum on Even Positions

题意:给一个数组,可以选择其中一个子数组翻转至多一次,求翻转后最大的“所有奇数位置的数的和”。

题解:很明显翻转的子数组必须长度为偶数才有用,这个看起来有点像用个前缀和搞搞就行了。具体来说就是枚举每个位置作为子数组的右端点,然后找一个位置作为左端点,要使得这个区间里的偶数位置和奇数位置的差异最大。

int n;
int a[200005];
 
void TestCase() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    ll sumodd = 0, sumeven = 0;
    for(int i = 1; i <= n; ++i) {
        if(i % 2 == 1)
            sumodd += a[i];
        else
            sumeven += a[i];
    }
    ll ans = sumodd;
    sumodd = 0, sumeven = 0;
    ll maxdif = 0, maxtmp = 0;
    for(int i = 1; i <= n; ++i) {
        if(i % 2 == 1)
            sumodd += a[i];
        else {
            sumeven += a[i];
            ll tmp = sumeven - sumodd + maxdif;
            maxtmp = max(maxtmp, tmp);
            maxdif = max(maxdif, sumodd - sumeven);
        }
    }
    sumodd = 0, sumeven = 0;
    maxdif = 0;
    for(int i = 1; i <= n; ++i) {
        if(i % 2 == 1) {
            sumodd += a[i];
            ll tmp = sumeven - sumodd + maxdif;
            maxtmp = max(maxtmp, tmp);
            maxdif = max(maxdif, sumodd - sumeven);
        } else
            sumeven += a[i];
    }
    ans += maxtmp;
    printf("%lld
", ans);
    return;
}
原文地址:https://www.cnblogs.com/KisekiPurin2019/p/13194451.html