codeforces 977非官方题解

本文精选了解答CF竞技平台上的五道题目,包括简单的模拟题、字符串统计问题、细节处理题、特殊排序算法及图论问题。每道题都附上了详细的解题思路和C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

打了差不多一年的cf发现自己是div3选手,也可能是div4+选手。

A

这一题就是简单的模拟题。

#include <iostream>

using namespace std;

long long main() {
    long long n, k;
    cin >> n >> k;
    for(long long i = 0; i < k; i++) {
        if(n % 10 == 0) {
            n /= 10;
        } else {
            n--;
        }
    }
    cout << n << endl;
    return 0;
}

B

这题是说要算一下哪个长度为2的子串出现次数最多,那么就统计一下吧。

#include <iostream>
using namespace std;

const int maxn = 30;
int cnt[maxn][maxn];
char ss[110];
int main() {
    int n;
    char c;
    scanf("%d", &n);
    scanf("%c", &c);
    scanf("%s", ss);
    for(int i = 0; i < n - 1; i++) {
        cnt[ss[i]-'A'][ss[i+1]-'A']++;
    }
    int mx = -1;
    int ii = -1;
    int ij = -1;
    for(int i = 0; i < 26; i++) {
        for(int j = 0; j < 26; j++) {
            if(mx < cnt[i][j]) {
                ii = i;
                ij = j;
                mx = cnt[i][j];
            }
        }
    }
    printf("%c%c", ii+'A', ij+'A');
    return 0;
}

C

这题的主要问题在于,可能有一点点小的细节。

另外还有一个问题是,如果不认真看题,可能会以为要找出一个严格大于k个数的数出来,而不是大于等于,至少我是这么看错的。看错的原因是,给的样例恰好是大于的。具体的细节请看代码。主要是要特判k==0。

代码写的有点蠢,但是还是过了。

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;
int a[maxn];
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    } 
    a[n+1] = inf;
    sort(a + 1, a + n + 1);
    if(k == 0) {
        if(a[1] > 1) {
            printf("1");
        } else {
            printf("-1");
        }
        return 0;
    }
    int cnt = 0;
    int t = a[k];
    int index = k;
    int tem = k;
    for(int i = k + 1; i <= n; i++) {
        if(a[i] <= t) {
            index++;
        } else {
            break;
        }
    }
    if(index == k) {
        printf("%d", a[k]);
    } else {
        printf("-1");
    }

    return 0;
}

D

这题我感觉我写了一个假算法啊,但是不知道为什么能pp。结果最后还是过了。。不是很懂

大概思路是,先预处理出一个后面可以接哪一个。b数组表示后面接哪个,c数组表示前面接哪个。

后来请教了一下,发现我这个算法没有问题。

事情是这样的,可以发现,对于这堆数,每个数都可以表示为a2x3ya∗2x∗3y ,只有x和y是变化的,而x变大,y变小,所以应该是一个双关键字排序,至于这个排序怎么排,也是有讲究的,比如说你真的算出x和y,然后排序,这个可以,但是实际上算出一个就够了,因为因数分解下来的那个a是一样的。由于题目保证了有解,所以我这里的做法,就恰好也保证了排序。

#include <iostream>
#include <vector>

using namespace std;
int n;
long long a[110];
int b[110];
int c[110];
int main() {
    long long n;
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(a[i] * 3 == a[j]) {
                b[j] = i;
                c[i] = j;
            } else if(a[i] * 2 == a[j]) {
                b[i] = j;
                c[j] = i;
            }
        }
    }
    int ss = -1;
    for(int i = 1; i <= n; i++) {
        if(c[i] == 0) {
            ss = i;
            break;
        }
    }
    while(ss != 0) {
        cout << a[ss] << " ";
        ss = b[ss];
    }
    return 0;
}

E

这道题的思路可以是这样的:

  1. 找出所有的联通块。这里可以用dfs来实现。
  2. 把判断每个联通块是不是都是合法的。我同学讲说看边数与点数是否相等,这个实现起来应该也不难,应该也是对的吧。还有一种做法是,判断每个点的度是否为2.
#include <stdio.h>
#include <vector>

using namespace std;
const int maxn = 2e5 + 10;

vector<int> v[maxn];
vector<int> g;
bool used[maxn];
void dfs(int x) {
    used[x] = true;
    g.push_back(x);
    for(int i : v[x]) {
        if(!used[i]) {
            dfs(i);
        }
    }
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    int x, y;
    for(int i = 0; i < m; i++) {
        scanf("%d%d", &x, &y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    int cnt = 0;
    for(int i = 1; i <= n; i++) {
        if(used[i]) {
            continue;
        }
        g.clear();
        dfs(i);
        bool ok = true;
        for(int ii : g) {
            if(v[ii].size() != 2) {
                ok = false;
                break;
            }
        }
        if(ok) {
            cnt++;
        }
    }
    printf("%d", cnt);
    return 0;
}

F

我的想法是这样的:

用dp[x]表示以x为结尾的串的长度,那么如果出现了一个数x,那么这时候更新dp[x],dp[x]=max(dp[x1]+1,dp[x])dp[x]=max(dp[x−1]+1,dp[x])

。然后有一种像我这样很蠢但是也是很简单易懂的做法是,这时候已经知道子串的所有元素信息了,那么在原来的串中找出来就行了。子串的第一个元素是xdp[x]+1x−dp[x]+1 ,然后依次在数列中找出大一个的数。

#include <stdio.h>
#include <map>
using namespace std;
const int maxn = 2e5 + 10;
map<int, int> dp;
int a[maxn];
int main() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        dp[a[i]] = max(dp[a[i]], dp[a[i]-1] + 1);
    }
    int mx_cnt = -1;
    int mi = -1;
    for(auto a : dp) {
        if(a.second > mx_cnt) {
            mx_cnt = a.second;
            mi = a.first;
        }
    }
    int cnt = mx_cnt;
    printf("%d\n", cnt);
    int num = mi - cnt + 1;
    for(int i = 1; i <= n; i++) {
        if(a[i] == num) {
            printf("%d ", i);
            num++;
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值