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;
}