Educational Codeforces Round 177 (Rated for Div. 2)

在这里插入图片描述

Educational Codeforces Round 177 (Rated for Div. 2)

A. Cloudberry Jam

思路:

1千克果子能生产2/3千克果酱,生产3千克果酱则需要2千克果酱,所以*2即可

code:

void solve() {   
    int x; cin >> x;
    cout << 2 * x << endl;
} 

B. Large Array and Segments

题意:

当前给出一个长度为n的数组a,数组b为数组a头尾相接k次而成,长度为n*k,现在求b数组中存在多少个左边界l,存在一个右边界r,使得l到r区间的元素和大于等于x

思路:

将数组b视为k段a,求出a数组的前缀和和sum,然后判断出在第几段时,会开始出现大于等于x的后缀即可,注意最多k段;

Code:

void solve() {
    int n, k,  x; cin >> n >> k >> x;
    vector<int> a(n), sum(n+1, 0);
    for(int i = 0;i < n;i ++){
        cin >> a[i];
        sum[i+1]=sum[i]+a[i];
    }
    int s = sum[n], ans = 0;
    for(int i = 0;i < n;i ++){
        int ca  = s - sum[i];
        int ttt;
        if (ca >= x) {
            ttt = 0;
        } else {
            ttt = (x - ca + s - 1) / s;
        }
        if (k <= ttt) continue;
        ans += (k - ttt);
    }
    cout << ans << endl;
}

C. Disappearing Permutation

题意:

当前有一个长度为n的排列p,以及一个操作序列d,第i次操作会使得 p d i = 0 p_{d_i} = 0 pdi=0

在第i次操作后,需要最少多少次操作可以使得数组恢复成一个任意顺序的排列,操作如下:

  • 将第i个位置的数替换为i

思路:

p i ! = i p_i != i pi!=i ,我们需要进行向上回溯类似并查集的方式寻找i的位置,回溯过程中的元素都需要进行单独的操作

首先记录每个元素所需要向上回溯的次数,即操作次数,回溯最终的位置即为祖宗结点

最后从第一个位置开始递增操作次数即可

Code:

void solve() {
    int n; cin >> n;
    for (int i = 1; i <= n; i ++) cin >> p[i];
    for (int i = 1; i <= n; i ++) cin >> d[i];

    vector<int> pos(n + 1, 0), find(n + 1, 0), sum(n + 1, 0);
    
    int now = 0;
    for (int i = 1; i <= n; i ++) {
        if (pos[i]) continue;
        now ++;
        int idx = i;

        while (pos[idx] == 0) {
            pos[idx] = 1;
            find[idx] = now;
            idx = p[idx];
        }

        int cnt = 0, st = i;
        idx = i;
        idx = p[idx];
        cnt ++;
        while (idx != st) {
            cnt ++;
            idx = p[idx];
        }

        sum[now] = cnt;
    }

    vector<int> ans;
    for (int i = 1; i <= n; i ++) pos[i] = 0;
    int ca = 0;
    for (int i = 1; i <= n; i ++) {
        int t = d[i];
        int id = find[t];
        if (!pos[id]) {
            ca += sum[id];
            pos[id] = 1;
        }
        cout << ca << ' ';
    } cout << endl;
}

D. Even String

题意:

给出26个字母的每个字母的数量,需要按照要求进行字符串的构造:

相同字符串直接的间隔必须是偶数

有多少种构造方案

思路:

思路原链接:https://zhuanlan.zhihu.com/p/1891280211527590056

首先明确一个多重排列数的计算公式,即n个序列中有ci个重复的数有多少种排列的方式:
n ! c 1 ! ⋅ c 2 ! ⋯ c k ! \frac{n!}{c_1! \cdot c_2! \cdots c_k!} c1!c2!ck!n!
而n!需要跟每个ci!进行逆元操作

就本题来说,同一字母只能全部放在奇数位或者偶数位

特判:显然,如果一个字母的数量单独在奇数位或者偶数位都不能容纳,则不可能完成

这里用一个背包来预处理一下字符的分组问题:

  • 我们需要将字母分成两组,奇数组和偶数组
  • 分配其中一个就可以,这里以奇数位为例,从后向前进行一个状态转移

然后就是每组的排列组合的问题:

  • 奇数位有odd个字符,那么就有odd!种排列方式,偶数同理
  • 代入多重排列的公式即可 n ! = o d d ! ∗ e v e n ! n! = odd!*even! n!=odd!even!

最后将排列数*分组数即为最终答案
(被创思了。。。。。。)

AC code:

int fact[N], invfact[N];

int qmi(int a, int k, int p) {
    int res = 1;
    while (k) {
        if (k & 1) res = res * a % p;
        a = a * a % p;
        k >>= 1;
    }
    return res;
} 

//多重排列数预处理,阶乘+逆元
void init_fact(int n) {
    fact[0] = invfact[0] = 1;
    for (int i = 1; i <= n; i ++) fact[i] = fact[i - 1] * i % MOD;
    invfact[n] = qmi(fact[n], MOD - 2, MOD);
    for (int i = n - 1; i >= 1; i --) invfact[i] = invfact[i + 1] * (i + 1) % MOD;
}

void solve() {
    vector<int> c(26);
    int sum = 0;
    for (int i = 0; i < 26; i ++) {
        cin >> c[i];
        sum += c[i];
    }
    int odd = (sum + 1) / 2, even = sum / 2;
    for (int i = 0; i < 26; i ++) {
        if (c[i] > odd && c[i] > even) {
            cout << 0 << endl;
            return;
        }
    }
    vector<int> dp(odd + 1, 0);
    dp[0] = 1;
    for (int i = 0; i < 26; i ++) {
        if (c[i] == 0) continue;
        for (int j = odd; j >= c[i]; j --) {
            dp[j] = (dp[j] + dp[j - c[i]]) % MOD;
        }
    }
    int ans = dp[odd];
    int ca = 1;
    for (int i = 0; i < 26; i ++) {
        ca = ca * invfact[c[i]] % MOD;
    }
    ans = ((ans * fact[odd] % MOD) * fact[even] % MOD) * ca % MOD;
    cout << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值