Educational Codeforces Round 116 (Rated for Div. 2) 解题报告

A - AB Balance

题意

给出一个只由字符 \(a\)\(b\) 组成的字符串 \(s\),求最少的修改使 \(s\)\(ab\) 的出现次数和 \(ba\) 的出现次数相等。

思路

\(s\)\(ab\)\(ba\) 都只会在连续段之间出现,并且交替出现,所以出现次数最多相差\(1\)。如果一共有奇数段,两者的出现次数肯定相等;如果有偶数段,只需要修改第一个字符就会变成奇数段。

所以只要出现次数不相等就修改第一个字符即可。

代码

#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 100 + 5;

char s[MAXN];
int n;

int main() {
    int T;

    scanf("%d", &T);
    while(T--) {
        scanf("%s", s + 1);
        n = strlen(s + 1);
        int cntAB = 0, cntBA = 0;
        for(int i = 1; i <= n - 1; i++) {
            if(s[i] == 'a' && s[i + 1] == 'b') {
                cntAB++;
            }
            if(s[i] == 'b' && s[i + 1] == 'a') {
                cntBA++;
            }
        }
        if(cntAB != cntBA) {
            if(s[1] == 'a') {
                s[1] = 'b';
            } else {
                s[1] = 'a';
            }
        }
        printf("%s\n", s + 1);
    }

    return 0;
}

B - Update Files

题意

\(n\) 台计算机,\(k\) 条数据线,每条数据线同一时刻只能连接两台计算机,问最少要几轮才能将第 \(1\) 台计算机上的文件传输到全部 \(n\) 台计算机上。

思路

当目前有文件的计算机总数少于 \(k\) 时,传输文件的计算机总数显然会每次翻倍,即 \(1\), \(2\), \(4\), \(8\), \(16\)…… 但如果当前有文件的计算机总数超过 \(k\),就只能每次增加 \(k\) 个,最后结果为 \(\lceil \frac{n - 2^m}{k} \rceil + m\) (\(2^m \leq n\)\(2^m > k\)\(2^{m - 1} \leq k\))。

代码

#include<cstdio>
using namespace std;

long long n, k;

int main() {
    int T;

    scanf("%d", &T);
    while(T--) {
        scanf("%lld%lld", &n, &k);
        if(n == 1) {
            printf("0\n");
            continue;
        }
        if(k == 1) {
            printf("%lld\n", n - 1);
            continue;
        }
        long long b = 1, cnt = 0;
        while(b <= k && b * 2 <= n) {
            b *= 2;
            cnt++;
        }
        cnt += (n - b) / k;
        if((n - b) % k != 0) {
            cnt++;
        }
        printf("%lld\n", cnt);
    }

    return 0;
}

C - Banknotes

题意

给出 \(n\) 种面值为 \(10^{a_i}\) 的纸币,求最少的金额使其无法用 \(k\) 张纸币表示出来。

思路

可以转换为找到最小的必须用 \(k + 1\) 种纸币表示出的金额,定义 \(s = k + 1\)。对于第 \(i\) 种纸币,令 \(s\) 减去 \(10^{a_{i + 1} - a_i} - 1\),如果不能减去或者已经到了面值最大的纸币,就直接输出当前的数,并在后面用 \(a_i\)\(9\) 填充。

代码

#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 10;
const int base[10] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};

bool vis[MAXN];
int a[MAXN], ans[MAXN];

int main() {
    int T;

    scanf("%d", &T);
    while(T--) {
        int n, k;

        scanf("%d%d", &n, &k);
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            vis[a[i]] = 1;
        }
        int s = k + 1, p = n;
        for(int i = 1; i <= n; i++) {
            if(i == n) {
                ans[i] = s;
                break;
            }
            if(s > base[a[i + 1] - a[i]] - 1) {
                ans[i] = base[a[i + 1] - a[i]] - 1;
                s -= base[a[i + 1] - a[i]] - 1;
            } else {
                ans[i] = s;
                p = i;
                break;
            }
        }
        printf("%d", ans[p]);
        for(int i = 1; i <= a[p]; i++) {
            printf("9");
        }
        printf("\n");
    }

    return 0;
}

E - Arena

题意

竞技场里有 \(n\) 个英雄,每个英雄的生命值为 \(a_i\)\(1 \leq a_i \leq x\))。每一次战斗中,每个英雄都会对其他所有英雄造成 \(1\) 点伤害,生命值降到 \(0\) 后英雄就会退场,如果若干次战斗后只剩下一个英雄,这个英雄就是赢家。

求有多少种生命值方案使得最后没有赢家,答案模 \(998244353\)。两种方案不同当且仅当至少有一个英雄的生命值不同。

思路

直接思考没有赢家的方案数似乎没什么思路,我们不妨考虑计算有赢家的方案数。

定义 \(f_{i, j}\) 为有 \(i\) 个英雄,生命值最大为 \(j\) 的方案数,就可以得到状态转移方程:

\[f_{i, j} = \sum_{k = 0}^{i - 1}f_{i - k, j - i + 1}(i - 1)^k C_i^k \]

其中 \(k\) 为这一次战斗退场的英雄数。

要注意的一点是,这道题的时限会导致一般的快速幂不能过。我采取的方式是对幂进行预处理,具体内容参见代码。

代码

#include<cstdio>
#include<algorithm>
using namespace std;

const int MAXN = 500 + 5;
const int MOD = 998244353;

long long C[MAXN][MAXN], f[MAXN][MAXN], qpow[MAXN][MAXN];
int n, m;

void init() {
	C[0][0] = 1;
	for(int i = 1; i <= n; i++) {
		C[i][0] = 1;
		for(int j = 1; j <= i; j++) {
			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
		}
	}
	for(int i = 1; i < MAXN; i++) {
		qpow[i][0] = 1;
		for(int j = 1; j < MAXN; j++) {
			qpow[i][j] = qpow[i][j - 1] * i % MOD;
		}
	}
}

int main() {
	scanf("%d%d", &n, &m);
	init();
	for(int j = 1; j <= m; j++) {
		f[1][j] = j;
		f[2][j] = j * (j - 1);
	}
	for(int i = 3; i <= n; i++) {
		for(int j = i; j <= m; j++) {
			for(int k = 0; k < i; k++) {
				f[i][j] = (f[i][j] + (f[i - k][j - i + 1] * qpow[i - 1][k]) % MOD * C[i][k] % MOD) % MOD;
			}
		}
	}
	printf("%lld\n", ((qpow[m][n] - f[n][m]) % MOD + MOD) % MOD);
	
	return 0;
}

(剩下的题等会就补)

(咕咕咕)

posted @ 2021-12-05 19:01  智子酱  阅读(30)  评论(0编辑  收藏  举报