【解题报告】Educational Codeforces Round 20
来源:互联网 发布:java中的double类型 编辑:程序博客网 时间:2024/05/16 07:00
题目链接
A. Maximal Binary Matrix(Codeforces 803A)
思路
本题的入手点是,先满足“字典序最小”的要求,再满足对称性(就比较容易了)。
由于题目要求解的“字典序”最小,所以可以先按照行从上到下,再按照列从左到右的顺序为矩形填上
- 当
i=j 时,由于当前填的位置位于对角线上,填上后肯定不会影响对称性,所以能填就填。什么是不能填的情况呢?如果填1 的次数没有剩余了的话就不能填了。 - 当
i!=j 时,由于当前填的位置不位于对角线上,所以如果要在(i,j) 网格内填数的话就得在(j,i) 网格内填数。有如下情况就不能填:剩余的填1 次数为1 ,(i,j) 网格内已经填过1 了。
代码
#include <bits/stdc++.h>using namespace std;const int maxn = 110;int n, k, a[maxn][maxn];// 输出矩阵void output() { for(int i = 1; i <= n ;i++) { for(int j = 1; j <= n; j++) { cout << a[i][j] << ' '; } cout << endl; }}int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> k; // k比格子还要多的时候 if(k > n * n) { cout << -1 << endl; return 0; } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { if(k == 0) { output(); return 0; } if(a[i][j] == 1) { continue; } // 填对角线位置 if(i == j) { a[i][j] = 1; k--; } // 填非对角线位置 else if(k >= 2) { a[i][j] = a[j][i] = 1; k -= 2; } } } if(k > 0) { cout << -1 << endl; } else { output(); } return 0;}
B. Distances to Zero(Codeforces 803B)
思路
本题的入手点在于将问题“找离某位置最近的
我们只考虑“找在数组中的位置的
代码
#include <bits/stdc++.h>using namespace std;const int maxn = 2e5 + 10, INF = 3e5;int n, last, a[maxn], b[maxn], c[maxn];int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for(int i = 0; i < n; i++) { cin >> a[i]; } // 从左到右扫描数组,寻找离位置i最近的左侧的0 last = -1; for(int i = 0; i < n; i++) { if(a[i] == 0) { b[i] = 0; last = i; } else if(last >= 0) { b[i] = i - last; } else { b[i] = INF; } } // 从右到左扫描数组,寻找离位置i最近的右侧的0 last = -1; for(int i = n - 1; i >= 0; i--) { if(a[i] == 0) { c[i] = 0; last = i; } else if(last >= 0) { c[i] = last - i; } else { c[i] = INF; } } // 输出答案 for(int i = 0; i < n; i++) { cout << min(b[i], c[i]) << ' '; } return 0;}
C. Maximal GCD(Codefoeces 803C)
思路
本题的入手点为考虑到数列的
这题要构造一个数列,其需要满足两个条件,一个是个数为
其中
最后就是实现的时候,由于输入的数据太大,需要注意溢出的问题。
代码
#include <bits/stdc++.h>using namespace std;typedef unsigned long long ll;vector <ll> divisor;ll n, k, ub, a, s, S;int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> k; // 按照以1首项1为公差的数列和求出k的上界 // 以避免下面出现的惩罚溢出 ub = 2 * (ll)(sqrt(1.0 * n) + 0.5); if(k > ub) { cout << -1 << endl; return 0; } // 预处理出n的约数 for(ll i = 1; i * i <= n; i++) { if(n % i > 0) { continue; } divisor.push_back(i); if(i != n / i) { divisor.push_back(n / i); } } // 对n的约数d for(ll d : divisor) { // 避免溢出,提前判断 if(k * (k - 1) / 2 >= n / d) { continue; } // 求出数列的和 s = k * (k - 1) * d / 2; if(n - s <= (k - 1) * d) { continue; } // 更新最大公约数最小的解 if(d > a) { a = d; S = s; } a = max(a, d); } if(a == 0) { cout << -1 << endl; return 0; } // 输出数列 for(ll i = 1; i < k; i++) { cout << a + (i - 1) * a << ' '; } cout << n - S << endl; return 0;}
D. Magazine Ad(Codeforces 803D)
思路
本题的入手点是,利用数据的“二分性”,将求最值的问题转化为验证某个值是否可行。
我们可以枚举最长的行的长度
但是由于
代码
#include <bits/stdc++.h>using namespace std;string s;vector <int> vec;int k, last, l, r;// 判断对给定的最长行的长度是否有解bool ok(int x) { int cnt = 0, sum = 0; // 对于每个“单词” for(int i = 0; i < vec.size(); i++) { if(vec[i] > x) { return false; } // 把该单词算在下行 if(sum + vec[i] > x) { sum = vec[i]; cnt++; } // 把该单词算在该行 else { sum += vec[i]; } } if(sum > 0) { cnt++; } return cnt <= k;}int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> k; getline(cin, s); getline(cin, s); last = 0; // 预处理处单词 for(int i = 1; i < s.size(); i++) { if(s[i] == ' ' || s[i] == '-') { vec.push_back(i - last + 1); last = i + 1; } } // 有可能还有最后一个单词 if(last <= s.size() - 1) { vec.push_back(s.size() - 1 - last + 1); } // 二分查找 l = -1; r = s.size(); while(r - l > 1) { int mid = (l + r) / 2; if(ok(mid) == true) { r = mid; } else { l = mid; } } cout << r << endl; return 0;}
E. Roma and Poker(Codeforces 803E)
思路
本题的入手点是,由“多阶段决策”这个特征想到用
在不考虑数据范围的情况下显然可以用搜索来解决,但是本题的数据范围显然不允许我们这样做。因此需要另辟蹊径。不妨令状态
虽然每次有这么多种状态转移情况,但是在每个状态
最后如果状态
j 有可能是负数。解决方案是将所有的j都加上一个偏移量,将原来j 可能落在的区间[−k,k] 映射到[lb,ub] 上去(lb≥0,ub≥0 )。- 由于在比赛的中途比赛不能终止(也就是一定要恰好在第
n 局结束),所以对于任意i<n ,在状态转移中,任意状态不能转移到状态(i,lb) 或(i,ub) 。
转移结束后还没有完成最终任务,我们还需要输出一个解。为了构造出一个解,我们可以用矩阵
代码
#include <bits/stdc++.h>using namespace std;const int maxn = 2010, mid = 1000;stack <char> ans;string s;int n, k, lb, ub, d[maxn][maxn], p[maxn][maxn];// 输的情况的状态转移void lose(int i, int j) { if(j == ub || !d[i - 1][j + 1]) { return; } d[i][j] = 1; p[i][j] = j + 1;}// 赢的情况的状态转移void win(int i, int j) { if(j == lb || !d[i - 1][j - 1]) { return; } d[i][j] = 1; p[i][j] = j - 1;}// 平局的情况的状态转移void draw(int i, int j) { if(d[i - 1][j] == 0) { return; } d[i][j] = 1; p[i][j] = j;}int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> k >> s; ub = mid + k; lb = mid - k; s = " " + s; d[0][mid] = 1; for(int i = 1; i <= n; i++) { for(int j = lb; j <= ub; j++) { // 比赛不能提前结束 if(i < n && (j == ub || j == lb)) { continue; } // 根据s[i]的值来决定状态的转移方式 if(s[i] == 'L') { lose(i, j); } else if(s[i] == 'D') { draw(i, j); } else if(s[i] == 'W') { win(i, j); } else { lose(i, j); draw(i, j); win(i, j); } } } // 无解的情况 if(!d[n][lb] && !d[n][ub]) { cout << "NO" << endl; return 0; } // 回溯构造解 int i = n; int j = d[n][lb] ? lb : ub; while(i > 0) { if(p[i][j] > j) { ans.push('L'); } else if(p[i][j] == j) { ans.push('D'); } else { ans.push('W'); } j = p[i--][j]; } // 逆序输出结果(这里用了一个栈) while(!ans.empty()) { cout << ans.top(); ans.pop(); } return 0;}
F. Coprime Subsequences(Codeforces 803F)
思路
本题的入手点是,运用分类的思想,正难则反的思想和容斥的思想来解决计数问题。
在开始描述解法之前先做一些铺垫。令
回到问题中。我们要求的是
a序列的满足性质“所有的数两两互质”的子序列的个数
“两两互质”听上去不那么清晰,可以将上述描述改成
a序列中满足性质“所有的数的最大公约数gcd为1”的子序列的个数
就算描述变成这样也还是不容易有思路,那么根据“正难则反”的思想,我们可以把上述描述进一步变形:
a序列中不满足性质“所有数的gcd为2或3或…或max{a_i, i <= n}”的子序列的个数
注意是“不满足”那个性质的子序列的个数。那么上述表述所描述的值就可以用容斥原理转化成求
其中
代码
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 1e5 + 5, mod = 1e9 + 7;bool vis[maxn];int n, a, f[maxn], mu[maxn], prime[maxn];ll ans, cnt[maxn];// 筛法预处理出莫比乌斯函数// 存在mu[]中void init(int N) { memset(vis, 0, sizeof(vis)); mu[1] = 1; int cnt = 0; for(int i = 2; i < N; i++) { if(!vis[i]) { prime[cnt++] = i; mu[i] = -1; } for(int j = 0; j < cnt && i * prime[j] < N; j++) { vis[i * prime[j]] = 1; if(i % prime[j]) { mu[i * prime[j]] = -mu[i]; } else { mu[i * prime[j]] = 0; break; } } }}// 枚举统计a数组中的各个数的约数的出现次数void divisor(int n) { for(int i = 1; i * i <= n; i++) { if(n % i == 0) { cnt[i]++; if(i != n / i) { cnt[n / i]++; } } }}// 计算快速幂的函数ll modPow(ll a, ll n, ll mm) { ll ans = 1; for(; n > 0; n >>= 1) { if(n & 1) { ans = (ans * a) % mm; } a = (a * a) % mm; } return ans;}int main() { ios::sync_with_stdio(false); cin.tie(0); init(maxn); cin >> n; for(int i = 1; i <= n; i++) { cin >> a; divisor(a); } // 计算上面提到的num()函数 for(int i = 1; i < maxn; i++) { f[i] = modPow(2, cnt[i], mod) - 1; f[i] = (f[i] + mod) % mod; } // 容斥原理 for(int i = 1; i < maxn; i++) { ans = (ans + mu[i] * f[i] + mod) % mod; } cout << ans << endl; return 0;}
其它题目略
- 【解题报告】Educational Codeforces Round 20
- [Updating] Educational Codeforces Round 20 解题报告
- Educational Codeforces Round 6 解题报告
- 【解题报告】Educational Codeforces Round 9
- 【解题报告】Educational Codeforces Round 12
- 【解题报告】Educational Codeforces Round 13
- 【解题报告】Educational Codeforces Round 15
- 【解题报告】Educational Codeforces Round 16
- 【解题报告】Educational Codeforces Round 14
- 【解题报告】 Educational Codeforces Round 19
- 【解题报告】Educational Codeforces Round 21
- 解题报告: Educational Codeforces Round 24 A,B,C
- 解题报告:Educational Codeforces Round 24 D,E,F
- Educational Codeforces Round 20
- Codeforces Educational Round 34划水报告
- CodeForces Round#35解题报告
- Codeforces Round #277.5 解题报告
- Codeforces-Round #340 解题报告
- spark一千篇旅游日记0006 之 DataFrame(二)
- 计算机人生思考
- .Net Error 当前不会命中断点。在 XXX.dll 中找到了 XXXX 的副本,但是当前源代码与 XXX.dll 中内置版本不同。若要允许...
- 蓝桥杯训练:爆搜——酒店招待
- can‘t create table phone.#sql-ea8_2(error150)
- 【解题报告】Educational Codeforces Round 20
- POJ3620Avoid The Lakes
- hibernate4缓存org.hibernate.cache.NoCacheRegionFactoryAvailableException
- 2017前端面试题总结
- 【面试考试中经常出现的集合类型总结】
- PHP操作Smarty
- L1-046. 整除光棍
- 国家出手在医疗机构开辟互联网医疗
- poj解题报告——poj 2575 Jolly Jumpers