codeforces 980非官方题解

来自一名div3+选手的题解,非完全版。

A Links and Pearls

这题本质上是一个数学问题。

#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    cin >> s;
    int cnt1 = 0;
    int cnt2 = 0;
    for(int i = 0; i < s.length(); i++) {
        if(s[i] == '-') {
            cnt1++;
        } else {
            cnt2++;
        }
    }
    if(cnt2 == 0) {
        printf("YES");
        return 0;
    } 
    if(cnt1 % cnt2 == 0) {
        printf("YES");
    } else {
        printf("NO");
    }
    return 0;
}

B Marlin

这题应该就是要构造一个对称的情况。我读错题目了,是说如果道路相邻,那么可以通过,而不是,所有的旅馆要连着。

那么按k的奇数偶数来排一下。如果是偶数,那么在(2,2), (2, n-1)开始一直从两边往中间摆。如果第2行摆满了,那么就摆第三行。然后要特判k取2×(n2) 的情况,这时候两个中间要填的。如果是奇数,那么就填一个中间的。

当然我自己不是这样写的,我的写法是,奇数是从中间往两边填,如果还没填够再从两边往中间填,但是偶数的情况下,直接第2行,第3行,都从第2个开始,往右边填。我这种写法是因为,我读错题目了。但是我这种做法确实是对的。首先偶数的情况,这是根据第二行与第三行的中间一行为对称轴的图形,那么显然是可以的。对于奇数的情况,(1,1)->(4, n)的最短路径数量与(1,n)->(4,1)的最短路径数量是一样的。然后(4,1)->(1,n)的最短路径数量与(1,n)->(4,1)的最短路径数量也是一样的。

#include <stdio.h>
#include <iostream>

using namespace std;

int n, k;
const int maxn = 110;
char a[maxn][maxn * 2];
int main() {
    scanf("%d%d", &n, &k);
    int max = (n - 2) * 2;
    if(k > max) {
        printf("NO");
        return 0;
    }

    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < n; j++) {
            a[i][j] = '.';
        }
    }
    if(k % 2 == 0) {
        for(int i = 1; i <= k / 2; i++) {
            a[1][i] = '#';
            a[2][i] = '#';
        }
    } else {
        a[1][n/2] = '#';
        k--;
        int i1 = n / 2 - 1;
        int i2 = n / 2 + 1;
        while(i1 > 0 && k > 0) {
            a[1][i1] = '#';
            a[1][i2] = '#';
            i1--;
            i2++;
            k -= 2;
        }
        i1 = 1;
        i2 = n - 2;
        while(k > 0) {
            a[2][i1] = '#';
            a[2][i2] = '#';
            i1++;
            i2--;
            k -= 2;
        }
    }
    printf("YES\n");
    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < n; j++) {
            printf("%c", a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

C Posterized

这个讲究的就是一个贪心。具体的实现可能稍微有点复杂。对于每一个数,我们要从当前数字出发,看能不能放最小值为这个数的组里面,一直到可能的最小值,不能把这个过程反过来。然后把可以放的组的最小值到当前数字,都放入组中。

#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn];
int b[maxn];
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    memset(b, -1, sizeof b);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for(int i = 1; i <= n; i++) {
        int x = a[i];
        if(b[x] != -1) {
            continue;
        }
        int s = x - k + 1;
        s = max(0, s);
        int t = x;
        for(int j = x; j >= s; j--) {
            if(b[j] == -1) {
                t = j;
            } else if(x - b[j] + 1 <= k) {
                t = b[j];
                break;
            } else {
                break;
            }
        }
        for(int j = t; j <= x; j++) {
            b[j] = t;
        }
    }
    for(int i = 1; i <= n; i++) {
        printf("%d ", b[a[i]]);
    }
    return 0;
}

D Perfect Groups

这题主要是要能读懂题目。

首先介绍了一个性质f,使得一个数组A对应的映射值为t,那么有f(A)=t。对于给定的数组B,有若干种取其连续子序列的做法,使得得到 Bx ,对于每个Bx ,有一个f(Bx)=tx ,要求统计tx 各出现了多少次。

对于这里的性质,我在代码的注释里面有详细的讲解。

#include<cstdio>
#include<map>

using namespace std;

#define ran 5555

int n;
int a[ran], s[ran], pre[ran];
map<int, int> mp;

int main() {
    // 获取输入并预处理输入
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        int f = (a[i] < 0) ? -1 : 1;
        a[i] *= f;
        // 如果一个数可以分解为a*b^x*... 其中x>=2,那么这时候x可以变成x-2,因为这个数a1与别的数a2相乘的时候如果为平方数,那么这个数a1除以一个平方数与a2相乘仍然是平方数
        // 所以这时候,实际上要统计的就是,在处理了之后,子序列中有多少个不一样的数,就要分成多少个组,对于0的情况下面有特判
        for (int j = 2; j * j <= a[i]; j++) {
            while (a[i] % (j * j) == 0) {
                a[i] /= j * j;
            }
        }
        a[i] *= f;
    }

    // 找出相邻的在i号数前面的一样的数的序号
    mp.clear();
    for (int i = 1; i <= n; i++) {
        pre[i] = mp[a[i]];
        mp[a[i]] = i;
    }

    // i表示连续子序列的开始位置(包括)
    for (int i = 1; i <= n; i++) {
        // cur表示除了0以外有多少个不同的数
        int cur = 0;
        // j表示连续子序列的结束位置(包括)
        for (int j = i; j <= n; j++) {
            // a[j] != 0表示,计算有多少个不同的数的时候,并不在意有没有0,因为0放哪个组里面都行。而如果只有0的话,下面的s[max(1, cur)]++;已经处理好了这种情况
            // pre[j] < i 表示,第j号元素前一次出现是在i之前,也就是说,在这里是第一次出现,也就是说,多出现了一个不同的数
            if (a[j] != 0 && pre[j] < i) {
                cur++;
            }
            // 如果有0个数,那么实际上也是要分成1组
            s[max(1, cur)]++;
        }
    }
    // 输出
    for (int i = 1; i <= n; i++) {
        printf("%d%c", s[i], " \n"[i >= n]);
    }

    return 0;
}

E The Number Games

据说用线段树或者树状数组都可以,但是我看了题解还是不能理解到底是怎么实现的,以后有机会补一补吧。

发布了293 篇原创文章 · 获赞 47 · 访问量 50万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览