Educational Codeforces Round 173 (Rated for Div. 2)题解(A-E)
Educational Codeforces Round 173 (Rated for Div. 2)题解(A-E)
你有一个价值为n的硬币,每次操作,如果n大于3,可以把硬币拆成两个价值为n/4的硬币
执行任意多次后,至多可以获得多少硬币?
显然,这是一个类似快速幂的行为
using i64 = long long;
void solve() {
i64 n; cin >> n;
i64 ans = 1;
while (n > 3) {
ans *= 2;
n /= 4;
}
cout << ans << endl;
}
复杂度
给定
,问如果 个d串起来形成的数字,可以被[1,3,5,7,9]里哪些数整除?
比如n=3,d=2,则串起来是222222
观察题。分类讨论,设串起来的数字是S
1 必然是可以整除的
3 如果d%3=0或者n>=3则可以整除,d%3=0是显然的;如果n>=3,则S长度是3!的倍数,显然可以被3整除。
5 要想末尾是5或者0,只能d=5
7 d=7是一种情况,另外打表可知3!=111111%7=0,而对于n>3,则S长度是3!的倍数,则有
9 类似3,更细节一点,d%9=0,或者d%3=0但是n>=3,或者n>=6(注意此时S长度是9的倍数)
using i64 = long long;
void solve() {
i64 n, d; cin >> n >> d;
vector<int> ans;
ans.push_back(1);
if (d % 3 == 0 || n >= 3) ans.push_back(3);
if (d % 5 == 0) ans.push_back(5);
if (d % 7 == 0 || n >= 3) ans.push_back(7);
if (d % 9 == 0 || d % 3 == 0 && n >= 3 || n >= 6) ans.push_back(9);
for (auto v : ans) cout << v << " ";
cout << endl;
}
复杂度
一个数组,元素均为-1或者1,但是至多可能存在一个元素是
顺序排列所有子数组的和(去重)
假设没有这个额外的元素,很容易意识到这是求最大/最小子数组和,由于数字绝对值为1,所以[min,max]之间必然全部出现,顺序打印即可。
简述下如何求最大子数组和
int ans = 0, sum = 0; //本题可以为空,否则ans应以INT_MIN为初值。
for (auto v : nums) {
sum += v;
ans = max(ans , sum);
if(sum < 0) sum = 0;
}
但是数组中可能存在一个非{-1,1}的元素x,如何处理?
答案由两部分构成,一部分是不包含x,即左右两侧的子数组和的并集。
即
另一部分是包含x,小心,包含x的话,必须是整体连续
即左部分+x+右部分是连续的。
左部分是以[x-1]为右端点的子数组值域范围
右部分是以[x+1]为左端点的子数组值域范围
注意两部分合并是
题目不难,写起来麻烦
using i64 = long long;
void solve() {
int n; cin >> n;
vector<int> nums(n);
int x = 0; //如果没有找到非{-1,1}的元素,那就取第一个元素即可。
for (int i = 0; i < n; i++) {
cin >> nums[i];
if (nums[i] != 1 && nums[i] != -1) x = i;
}
//计算[L,R]的子数组最大值和最小值
auto dfs = [&](int L, int R, bool calcMin = true)->int {
int res = 0, sum = 0;
for (int i = L; i <= R; i++) {
int v = nums[i];
sum += v;
res = calcMin ? min(res, sum) : max(res, sum);
if (calcMin && sum > 0) sum = 0;
if (!calcMin && sum < 0) sum = 0;
}
return res;
};
int minSum = min(dfs(0, x - 1, true), dfs(x + 1, n - 1, true));
int maxSum = max(dfs(0, x - 1, false), dfs(x + 1, n - 1, false));
set<int> set;
for (int i = minSum; i <= maxSum; i++) set.insert(i);//不包含x的部分
//以下标x-1为右端点的子数组范围
int leftMin = 0, leftMax = 0, leftSum = 0;
for (int i = x - 1; i >= 0; i--) {
leftSum += nums[i];
leftMin = min(leftMin, leftSum);
leftMax = max(leftMax, leftSum);
}
//以下标x+1为左端点的子数组范围
int rightMin = 0, rightMax = 0, rightSum = 0;
for (int i = x + 1; i < n; i++) {
rightSum += nums[i];
rightMin = min(rightMin, rightSum);
rightMax = max(rightMax, rightSum);
}
minSum = leftMin + rightMin + nums[x];
maxSum = leftMax + rightMax + nums[x];
for (int i = minSum; i <= maxSum; i++) set.insert(i);//包含x的部分
cout << set.size() << endl;
for (auto v : set) {
cout << v << " ";
}
cout << endl;
}
复杂度最优可以是
这里为了方便,直接使用了set去重和排序,此时复杂度是
已知区间L,R,和一个整数G,
。
求整数对A B,要求,且GCD(A,B)=G,且要最大化B-A。
最关键的点在于GCD(A,B)=G的意义,这表示A,B都是G的倍数,且
然后就是,给定两个数P,Q,要想保证区间[P, P+d]和区间[Q,Q+d]中存在互质数对,d的取值可以很小...
证明是不会证明的,反正暴力过了...我才不会说取值从1000降到100又降到30才过的...
using i64 = long long;
i64 gcd(i64 a, i64 b) { return b == 0 ? a : gcd(b, a % b); }
void solve() {
i64 L, R, G; cin >> L >> R >> G;
i64 mL = (L + G - 1) / G; //mL*G是[L,R]中G的倍数最小值
i64 mR = R / G; //mR*G是[L,R]中G的倍数最大值
i64 r1 = -1, r2 = -1;
i64 mx = -1;
for (i64 i = mL; i <= mR && i < mL + 30; i++) {
for (i64 j = mR; j >= mR - 30 && j >= i; j--) {
if (gcd(i, j) == 1) {
if (j - i > mx) {
r1 = i * G;
r2 = j * G;
mx = j - i;
}
}
}
}
cout << r1 << " " << r2 << endl;
}
复杂度不确定,就记作
给定矩阵A,B 定义两种操作
任选矩阵A一行,任选 ,对这一行所有元素执行
任选矩阵A一列,任选 ,对这一列所有元素执行
问,能否将A转换为B
很好的题。
首先要能意识到拆位操作,因为可以选择x=1<<k,则不同bit之间是操作无关的。
问题转换为A,B均为0 1矩阵。
举一个最简明的例子
A左下角的1,必须通过行变换为0,才能与B一致
然后A右下角再执行列变换为1
然后A右上角再执行行变换为0
最后当A左上角执行列变换为1时,此时左下角从0变回了1。
产生了循环,所以A是无法变成B的。
这启发我们,行列的变换不能产生环。
具体的:
- 我们先枚举所有位置,如果
和 不相等,则必须产生一次变换。
如果 为0,则是行变换,否则是列变换。 - 一轮枚举结束后,我们考虑某一变换的行或列,以行为例,该行的每个元素变换后都会变成0
那么我们需要检查B的对应行是否全为0, 有不是0的,则需要对该列执行列变换。 - 如果有新的变换添加进来,则需要重复执行上述检查操作,直到无新增变换为止。
- 此时按照所有产生操作的的行和列,和对应的
的值,进行连边处理。
如果 ,则 行需要向 列连边,表示需要先执行行变换,再执行列变换。
反之从 列向 行连边。 - 最后就是简单的有向图判环。这可以用dfs染色,拓扑排序,并查集等手段处理。
以下是完整代码。
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve()
{
int n, m; cin >> n >> m;
vector<vector<int>> A(n), B(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int v; cin >> v;
A[i].push_back(v);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int v; cin >> v;
B[i].push_back(v);
}
}
//拆位处理
for (int k = 0; k <= 30; k++) {
//第一轮枚举 将变化的行列加入队列中。
queue<int> q;
vector<int> flagX(n), flagY(m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!flagX[i] && (A[i][j] >> k & 1) && !(B[i][j] >> k & 1)) {
flagX[i] = 1;
q.push(i);
}
if (!flagY[j] && !(A[i][j] >> k & 1) && (B[i][j] >> k & 1)) {
flagY[j] = 1;
q.push(n + j);
}
}
}
//循环检测是否有新变换加入,直到无新增为止
while (!q.empty()) {
int u = q.front();
q.pop();
if (u < n) {
for (int j = 0; j < m; j++) {
if (!flagY[j] && (B[u][j] >> k & 1)) {
flagY[j] = true;
q.push(j + n);
}
}
}
else {
for (int i = 0; i < n; i++) {
if (!flagX[i] && !(B[i][u - n] >> k & 1)) {
flagX[i] = true;
q.push(i);
}
}
}
}
//有向图建立
vector<vector<int>> g(n + m);
vector<int> deg(n + m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (flagX[i] && !(B[i][j] >> k & 1)) {
deg[j + n]++;
g[i].push_back(j + n);
}
if (flagY[j] && (B[i][j] >> k & 1)) {
deg[i]++;
g[j + n].push_back(i);
}
}
}
//以下是拓扑排序求环
int count = n + m;
queue<int> que;
for (int i = 0; i < n; i++) if (deg[i] == 0) que.push(i);
for (int j = 0; j < m; j++) if (deg[j + n] == 0) que.push(j + n);
while (!que.empty()) {
int u = que.front();
que.pop();
count--;
for (auto v : g[u]) {
deg[v]--;
if (deg[v] == 0) que.push(v);
}
}
//只要有某一位不合法返回NO
if (count) {
cout << "NO" << endl;
return;
}
}
cout << "YES" << endl;
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1; cin >> t;
while (t-- > 0) {
solve();
}
}
复杂度为
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具