【解题报告】Educational Codeforces Round 14

本文详细解析了Codeforces Round #691(教育赛,Div. 2)的六道题目,包括Fashion in Berland、s-palindrome、Exponential notation、Swaps in Permutation、Xor-sequences和Couple Cover。通过思路分析和代码展示,帮助读者理解每道题目的解法。

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

题目链接


A. Fashion in Berland(Codeforces 691A)

思路

根据题意,只需要检查输入的序列中0的个数是否为1即可。另外需要特判夹克上只有一颗扣子的情况。

代码

#include <bits/stdc++.h>
using namespace std;

int n, a, cnt;

int main() {
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        scanf("%d", &a);
        cnt += a;
    }
    if(n == 1) {
        puts(cnt == 1 ? "YES" : "NO");
    }
    else {
        puts(cnt == n - 1 ? "YES" : "NO");
    }
    return 0;
}

B. s-palindrome(Codeforces 691B)

思路

先建立一个映射表,将所有英文字母映射到与其具有对称结构的英文字母上,然后将字符串反转,再将其用映射表映射成另一个字符串。判断构造出来的字符串是否与原串相同即可。(下面代码构造映射表的方式太麻烦,用两个字符串来表示映射关系会更好)

#include <bits/stdc++.h>
using namespace std;

bool ok;
string s, t;
map <int, int> m;

void init() {
    for(int i = 'A'; i <= 'z'; i++) {
        m[i] = '#';
    }
    m['A'] = 'A'; m['H'] = 'H'; m['I'] = 'I';
    m['M'] = 'M'; m['O'] = 'O'; m['T'] = 'T';
    m['U'] = 'U'; m['V'] = 'V'; m['W'] = 'W';
    m['X'] = 'X'; m['Y'] = 'Y'; m['b'] = 'd';
    m['d'] = 'b'; m['o'] = 'o'; m['p'] = 'q'; 
    m['q'] = 'p'; m['v'] = 'v'; m['w'] = 'w'; 
    m['x'] = 'x';
}

int main() {
    init();
    cin >> s;
    t = s;
    reverse(t.begin(), t.end());
    ok = true;
    for(int i = 0; i < s.size(); i++) {
        if(m[t[i]] != s[i]) {
            ok = false;
        }
    }
    puts(ok ? "TAK" : "NIE");
    return 0;
}

C. Exponential notation(Codeforces 691C)

思路

  • 如果输入的是小于 1 的数的话,就从小数点开始往后查找,找到第一个非零的数位,假设这个数位上的数是 x ,根据 x 与小数点的位置可以确定 aEb 中的 b 是多少。最后先后将 x 、小数点和 x 之后的非零数字拼接起来就是 aEb 中的 a
  • 如果输入的是大于或等于 1 的数的话,就从小数点开始向前查找,原理与上一个情况相同。

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 5;
char str1[maxn];
char str2[maxn];

int main() {
    scanf("%s",str1);
    int len_str1 = strlen(str1);
    int point = len_str1;
    for(int i = 0; i < len_str1; i++) {
        if(str1[i]=='.') {
            point = i;
            break;
        }
    }
    int flag_point;
    int len_str2;
    for(int i=0; i<len_str1; i++) {
        if(str1[i]>'0'&&str1[i]<='9') {
            str2[0]=str1[i];
            str2[1]='.';
            flag_point=i+1;
            len_str2=1;
            for(int j=i+1; j<len_str1; j++)
                if(str1[j]>='0'&&str1[j]<='9')
                    str2[++len_str2]=str1[j];
            break;
        }
    }
    for(int i=len_str2; i>=1; i--) {
        if(str2[i]=='0'||str2[i]=='.') {
            len_str2--;
        }
        else {
            break;
        }
    }
    for(int i=0; i<=len_str2; i++) {
        printf("%c",str2[i]);
    }
    if(point != flag_point) {
        point>flag_point?printf("E%d\n",point-flag_point):printf("E%d\n",point-flag_point+1);
    }
    return 0;
}

D. Swaps in Permutation(Codeforces 691D)

思路

当允许两个位置进行交换操作时,这两个位置就发生了联系,而且在思考中我们可以发现这种联系是可交换的,也是可传递的。于是我们就可以用无向图表示这种联系。其中点用来表示位置,边用来表示位置之间的联系。
建图完毕后,图必然由若干个连通分量组成。连通分量的意义是,该分量中的位置上的数可以任意排序。根据题意,我们要让同一个分量中大的数排在前面,小的数排在后面,并且对每个分量做这样的排序。那么在完成排序后,总序列一定是符合条件的。
实现上,连通的信息可以用并查集维护(因为除连通信息外不需要用图的其它信息了),除此之外还需要一个从连通分量映射到该分量中所有点的表。这样就能快速地从连通分量中将数提取出来,排序后再放回。

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 10;
int n, m, u, v, a[maxn], p[maxn];
vector <int> vec, G[maxn];

void init() {
    for(int i = 1; i <= n; i++) {
        p[i] = i;
    }
}

int Find(int x) {
    return x == p[x] ? x : p[x] = Find(p[x]);
}

void Union(int x, int y) {
    x = Find(x);
    y = Find(y);
    if(x == y) {
        return;
    }
    p[x] = y;
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    init();
    while(m--){
        scanf("%d%d", &u, &v);
        Union(u, v);
    }
    for(int i = 1; i <= n; i++) {
        G[Find(i)].push_back(i);
    }
    for(int i = 1; i <= n; i++) {
        if(G[i].size() == 1) {
            continue;
        }
        vec.clear();
        for(int idx : G[i]) {
            vec.push_back(a[idx]);
        }
        sort(vec.begin(), vec.end(), [] (int x, int y) { return x > y; });
        for(int j = 0; j < G[i].size(); j++) {
            int idx = G[i][j];
            a[idx] = vec[j];
        }
    }
    for(int i = 1; i <= n; i++) {
        printf("%d ", a[i]);
    }
    return 0;
}

E. Xor-sequences(Codeforces 691E)

思路

本题看上去是个复杂的计数问题,实则不然。对于题中的“异或序列”而言,其中的数字的排列方式是由异或模3这种运算决定的。也就是说数字的排列可以看成某种关系。数字 a b 满足 abmod3=0 a 才能排在 b 前面(或者相反)。
用图的观点看待这种关系的话,如果代表 a 的点和代表 b 的点满足前述关系,则它们之间有一条无向边相连。也就是说一个长度为 k 的排列对应了图中的一条长度为 k 的路径。如果我们将图用邻接矩阵表示出来,设该矩阵的 k 次幂为 ans ,则 ans[i][j] 恰好表示从点 i 到点 j 的长度为 k 的路径有多少条(不必太过纠结,当成结论记忆就好)。
于是问题就转化成了求邻接矩阵的 k 次幂。在 k 很大的情况下用矩阵快速幂就可以实现。

代码

#include <bits/stdc++.h>
using namespace std;

const int mod  = 1e9 + 7;
const int maxn = 105;
typedef long long ll;

template <class T>
struct matrix {
    int n, m;
    T a[maxn][maxn];
    matrix(int n = 0, int m = 0): n(n), m(m) {
        memset(a, 0, sizeof(a));
    }
    matrix modMul(matrix &b, T mod) const {
        matrix tmp(n, b.m);
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < b.m; j++) {
                for(int k = 0; k < m; k++) {
                    T t = a[i][k] * b.a[k][j] % mod;
                    tmp.a[i][j] = (tmp.a[i][j] + t) % mod;
                }
            }
        }
        return tmp;
    }
    matrix modPow(T e, T mod) const {
        matrix a = *this, tmp(n, n);
        for(int i = 0; i < n; i++) {
            tmp.a[i][i] = 1;
        }
        for(; e > 0; e >>= 1) {
            if(e & 1) {
                tmp = tmp.modMul(a, mod);
            }
            a = a.modMul(a, mod);
        }
        return tmp;
    }
};

int n;
ll k, sum, a[maxn];

int main() {
    cin >> n >> k;
    if(k == 1) {
        cout << n << endl;
        return 0;
    }
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    matrix <ll> mat(n, n), ans(n, n);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(bitset<64>(a[i] ^ a[j]).count() % 3 == 0) {
                mat.a[i-1][j-1] = 1;
            }
        }
    }
    ans = mat.modPow(k - 1, mod);
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            sum = (sum + ans.a[i][j]) % mod;
        }
    }
    cout << sum << endl;
    return 0;
}

F. Couple Cover(Codeforces 691F)

思路

先将问题转化为求比 p 小的数对的个数。先统计每个数的出现次数。再暴力枚举数对中的两个数,因为 p 的最大值是 3e6 ,所以枚举出来的两个数的乘积也不超过 3e6 。将枚举的数的乘积限制一下,枚举的复杂度就降为 O(nlogn) 了。出现 logn 是因为这个式子:

ni=11i=O(log(n))

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxa = 3e6;
ll n, a, all, m, q, num[maxa], sum[maxa];

int main() {
    scanf("%I64d", &n);
    all = n * (n - 1);
    while(n--) {
        scanf("%I64d", &a);
        m = max(m, a);
        num[a]++;
    }
    for(ll i = 1; i <= m; i++) {
        for(ll j = 1; j <= m; j++) {
            if(i * j > maxa) {
                break;
            }
            if(i == j) {
                sum[i*i] += num[i] * (num[i] - 1);
            }
            else {
                sum[i*j] += num[i] * num[j];
            }
        }
    }
    for(ll i = 2; i <= maxa; i++) {
        sum[i] += sum[i-1];
    }
    scanf("%I64d", &q);
    while(q--) {
        scanf("%I64d", &a);
        printf("%I64d\n", all - sum[a-1]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值