【解题报告】Educational Codeforces Round 13

这篇博客详细介绍了Educational Codeforces Round 13的比赛题目,包括A到E五道题目。博主分享了每道题目的解题思路和代码实现,涉及数学、日历计算、贪心算法、矩阵快速幂和动态规划等概念。
摘要由CSDN通过智能技术生成

题目链接


A. Johny Likes Numbers(Codeforces 678A)

思路

首先直接枚举 k 的倍数是不可能的,数据规模告诉我们必须通过直接计算得到结果。下面分类,当 k 能整除 n 时,答案为 n+k 。当 k 不能整除 n 时,计算 tmp=nmodk tmp 表示n比“不超过 n k 的最大的倍数”大多少。 ktmp 的结果表示n到“比n大的k的倍数中最小的那个数”的距离,那么 n+ktmp 就是答案。

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

typedef long long ll;
ll n, k, tmp;

int main() {
    cin >> n >> k;
    tmp = n % k;
    if(tmp == 0) {
        cout << n + k << endl;
    }
    else {
        cout << n - tmp + k << endl;
    }
    return  0;
}

B. The Same Calendar(Codeforces 678B)

思路

首先想象下面的场景,我们将今年的每月分的日历拼接起来形成年历,然后将从今年开始后的 4000 年的年历拼接起来形成一份超长的年历。因为 365mod7=2 ,所以根据那份超长的日历,若今年是平年且今年的第一天为周一,那么明年的第一天为周二,若今年是闰年且今年的第一天为周一,那么明年的第一天为周三。所以我们当我们一年一年这样算下去,碰到某年的第一天为周一且该年的“润性”与今年一致时,该年的日历就和今年是一致的。

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

const int maxn = 1e6;
int n, cur, ans, init;

bool isLeap(int x) {
    if(x % 400 == 0) {
        return true;
    }
    if(x % 4 == 0) {
        return x % 100;
    }
    return false;
}

int main() {
    cin >> n;
    cur = 0;
    init = isLeap(n);
    for(int i = n + 1; i < maxn; i++) {
        if(isLeap(i - 1) == false) {
            cur = (cur + 1) % 7;
        }
        else {
            cur = (cur + 2) % 7;
        }
        if(cur == 0 && isLeap(i) == init) {
            ans = i;
            break;
        }
    }
    cout << ans << endl;
    return  0;
}

C. Joty and Chocolate(Codeforces 678C)

思路

只能被 a 整除的编号的瓦 A 和只能被 b 整除的编号的瓦 B 分别只能被涂上红色和蓝色,而能被 a,b 两个数整除的编号的瓦 C 既能被涂上红色也能被涂上蓝色,而贪心的我们肯定会选择巧克力多的颜色涂。所以我们只要分别算出 AB C 的数量就好了。它们分别是 n/aC,n/bC n/lcm(a,b) lcm(x,y) 表示 x y 的最小公倍数)。

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

typedef long long ll;
ll n, a, b, p, q, da, db, dc, ans;

ll lcm(ll a, ll b) {
    return a / __gcd(a, b) * b;
}

int main() {
    cin >> n >> a >> b >> p >> q;
    da = n / a;
    db = n / b;
    dc = n / lcm(a, b);
    ans = p * (da - dc) + q * (db - dc) + max(p, q) * dc;
    cout << ans << endl;
    return  0;
}

D. Iterated Linear Function(Codeforces 678D)

思路

题目的意思就是求当 a0=xan=Aan1+B 时的 an ,这里相当于 a0 进行了 n 次线性变换,对于这种 n 次线性变换且 n 特别大的问题,可以用矩阵快速幂解决,单次线性变换可以用矩阵乘法表示为:

(an1)=(A0B1)×(an11)

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

typedef long long ll;
const ll mod = 1e9 + 7;
ll a, b, n, x;

struct matrix {
    int n, m;
    ll a[2][2];
    matrix(int n = 2, int m = 2): n(n), m(m) {
        memset(a, 0, sizeof(a));
    }
    matrix mod_mul(matrix &b, ll 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++) {
                    tmp.a[i][j] = (tmp.a[i][j] + a[i][k] * b.a[k][j] % mod) % mod;
                }
            }
        }
        return tmp;
    }
    matrix mod_pow(ll nn, ll mod) const {
        matrix a = *this, tmp(n, n);
        for(int i = 0; i < n; i++) {
            tmp.a[i][i] = 1;
        }
        for(; nn > 0; nn >>= 1) {
            if(nn & 1) {
                tmp = tmp.mod_mul(a, mod);
            }
            a = a.mod_mul(a, mod);
        }
        return tmp;
    }
}mat, ans;

int main() {
    cin >> a >> b >> n >> x;
    mat.a[0][0] = a;
    mat.a[0][1] = b;
    mat.a[1][0] = 0;
    mat.a[1][1] = 1;
    ans = mat.mod_pow(n, mod);
    cout << (x * ans.a[0][0] + ans.a[0][1]) % mod << endl;
    return  0;
}

E. Another Sith Tournament(Codeforces 678E)

思路

最朴素的想法是枚举人物出场顺序,然后就能算出主人公获胜的概率。然而这样的想法是不可行的。因为阶乘复杂度是本题无法承受的。怎么办呢?按照经验,这种数据规模较小的朴素思想为枚举排列的题目可以通过状态压缩的集合表示问题解决过程中的阶段,从而记录最优子结构,使问题能通过动态规划的思想得以解决。假设我们用将变量i表示当前阶段,其二进制形式表示当前哪些人已经死了( 0 ),哪些人还活着( 1 ),那么 d[i] 表示在阶段变量为i时,主人公能获胜的概率。显然目前知道的信息是 d[1]=1 。现在设计状态转移方程。显然,当前状态的dp值依赖于当前状态的下一个状态的 dp 值。这样我们的状态表示法是无法然状态转移的。因为不能知道当前是谁还活着,状态就无法转移。那么我们再为每个阶段增加一个状态变量 j ,表示当前阶段下谁在守擂,总的状态为 (i,j) 。现在可以设计状态转移方程了。 d[i][j]=max(d[i][j],j 继续守擂后主人公最后胜出的概率 +j 守擂失败后主人公最后胜出的概率 ) ,为了计算最大值,我们枚举攻擂方 k ,状态转移方程为 d[i][j]=max(d[i][j],p[j][k]×d[i(1<<k)][j]+p[k][j]×d[i(1<<j)][k]) 。最后对所有的 jd[(1<<n)1][j] 的最大值就是最后的答案。

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

const int maxn = 18;
int n, x, y, ub;
double tmp, ans, p[maxn][maxn], d[1<<maxn][maxn];

int main() {
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            scanf("%lf", &p[i][j]);
        }
    }
    ub = 1 << n;
    d[1][0] = 1.0;
    for(int i = 2; i < ub; i++) {
        for(int j = 0; j < n; j++) {
            x = 1 << j;
            if(i & x == 0) {
                continue;
            }
            for(int k = 0; k < n; k++) {
                y = 1 << k;
                if(i & y == 0) {
                    continue;
                }
                tmp = p[j][k] * d[i^y][j] + p[k][j] * d[i^x][k];
                d[i][j] = max(d[i][j], tmp);
            }
        }
    }
    for(int j = 0; j < n; j++) {
        ans = max(ans, d[ub-1][j]);
    }
    printf("%.10f\n", ans);
    return 0;
}

(其他题目略)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值