Educational Codeforces Round 171 (Rated for Div. 2)
写在前面
比赛地址:https://codeforces.com/contest/2026。
因为太困了决定睡大觉,于是赛时 unrated register 只写了 DE。
然而十一点半上床还是失眠到一点半睡的太搞了呃呃
A 签到
B 暴力
限定了只能操作白色格子,显然对于 为偶数只能直接操作, 为奇数一定会多操作一个格子。
一个显然的结论是,对于 为奇数的情况,多操作的格子的位置一定为某个 前驱或后继(即 或 )。
这等价于删去某个位置(即与多修改的格子匹配的位置)之后,求前面所有相邻的位置对的差分,以及后面所有相邻的位置对的差分的最大值。要是不注意数据范围的话可能就会去写这个有一点边界需要调的 or 的东西了。
然而 可过,于是直接枚举修改位置即可。
复制复制// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 2010; //============================================================= int n; LL a[kN]; //============================================================= //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { std::cin >> n; LL ans = 0; for (int i = 1; i <= n; ++ i) std::cin >> a[i]; if (n % 2 == 0) { for (int i = 2; i <= n; i += 2) ans = std::max(ans, a[i] - a[i - 1]); } else { ans = a[n]; std::set<LL> s; for (int i = 1; i <= n; ++ i) s.insert(a[i] - 1), s.insert(a[i] + 1); for (auto p: s) { std::vector<LL> b; for (int i = 1; i <= n; ++ i) if (a[i] < p) b.push_back(a[i]); b.push_back(p); for (int i = 1; i <= n; ++ i) if (a[i] > p) b.push_back(a[i]); LL k = 0; for (int i = 1; i <= n; i += 2) k = std::max(k, b[i] - b[i - 1]); ans = std::min(ans, k); } } std::cout << ans << "\n"; } return 0; }
做法:
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 2010; //============================================================= int n; LL a[kN], maxpred[kN], maxsufd[kN]; //============================================================= //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { std::cin >> n; LL ans = 0; for (int i = 1; i <= n; ++ i) std::cin >> a[i]; if (n % 2 == 0) { for (int i = 2; i <= n; i += 2) ans = std::max(ans, a[i] - a[i - 1]); } else { maxpred[1] = maxsufd[n] = maxsufd[n + 1] = 0; for (int i = 2; i <= n; ++ i) { maxpred[i] = std::max(maxpred[i - 2], a[i] - a[i - 1]); } for (int i = n - 1; i; -- i) { maxsufd[i] = std::max(maxsufd[i + 2], a[i + 1] - a[i]); } ans = a[n]; std::set<LL> s; for (int i = 1; i <= n; ++ i) s.insert(a[i]); for (int i = 1; i <= n; ++ i) { if (i % 2 == 0 || (s.count(a[i] - 1) && s.count(a[i] + 1))) continue; ans = std::min(ans, std::max(1ll, std::max(maxpred[i - 1], maxsufd[i + 1]))); } } std::cout << ans << "\n"; } return 0; }
C 反悔贪心
每个物品可以购买的时间范围是一个后缀,即越往后对前面元素的限制越少,考虑反悔贪心。
考虑记必须花钱买的物品数量,以及哪些物品是可以免费获得的。
然后枚举每一天,若当天无法到达商店则该物品必须花钱买;若当天可以到达商店:
- 若之前有必须买的物品,则今天一定买之前的某个物品,今天的物品免费获得;
- 若之前无必须买的物品,但之前免费获得了某个物品,考虑反悔,则今天的策略为:将该物品变为买的,并将今天的物品免费获得;
于是考虑用小根堆维护免费获得的物品,反悔贪心即可即可。
总时间复杂度 级别。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long //============================================================= int n; std::string s; //============================================================= //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { std::cin >> n; std::cin >> s; s = "$" + s; LL ans = 0, cnt = 0; std::priority_queue<int> q; for (int i = 1; i <= n; ++ i) { ans += i; if (s[i] == '0') { ++ cnt; } else { if (cnt) { ans -= i; q.push(-i); -- cnt; } else if (!q.empty()) { ans -= i; ans += -q.top(); ++ cnt; q.pop(), q.push(-i); } else { ++ cnt; } } } std::cout << ans << "\n"; } return 0; }
D 枚举,分块,推式子
经典题,写了一个类似分块的做法。
考虑按照 的左端点 对构造的数列 分块,每次询问分别处理左右的散块和中间的整块。
对于中间的整块考虑预处理,记: 表示 的前缀和, 表示 的二维前缀和,记 表示整块的和。 即 ,且显然可以递推:
再对 做个前缀和即可快速询问整块的和。
对于左右散块推下式子,记当前位于块 ,且 分别为该块的第 个元素,则有:
每次找到端点所在块使用二分,总时间复杂度 级别。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 3e5 + 10; //============================================================= int n, a[kN]; LL L[kN], R[kN], sum[kN], sumsum[kN]; LL S[kN], sumS[kN]; //============================================================= int get(LL pos_) { int ret = 1; for (int l = 1, r = n; l <= r; ) { int mid = (l + r) >> 1; if (pos_ >= L[mid]) { ret = mid; l = mid + 1; } else { r = mid - 1; } } return ret; } LL query(LL l_, LL r_) { int bl = get(l_), br = get(r_); LL ret = 0; if (bl == br) { int pl = l_ - L[bl] + 1, pr = r_ - L[bl] + 1, len = r_ - l_ + 1; ret = sumsum[bl + pr - 1] - sumsum[bl + pl - 1 - 1] - len * sum[bl - 1]; } else { ret = sumS[br - 1] - sumS[bl]; ret += query(l_, R[bl]) + query(L[br], r_); } return ret; } //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); std::cin >> n; for (int i = 1; i <= n; ++ i) std::cin >> a[i]; for (int i = 1; i <= n; ++ i) sum[i] = sum[i - 1] + a[i]; for (int i = 1; i <= n + 1; ++ i) L[i] = R[i - 1] + 1, R[i] = R[i - 1] + (n - i + 1); for (int i = 1; i <= n; ++ i) sumsum[i] = sumsum[i - 1] + sum[i]; S[1] = sumsum[n]; for (int i = 2; i <= n; ++ i) S[i] = S[i - 1] - (n - i + 2) * a[i - 1]; for (int i = 1; i <= n; ++ i) sumS[i] = sumS[i - 1] + S[i]; int q; std::cin >> q; while (q --) { LL l, r; std::cin >> l >> r; std::cout << query(l, r) << "\n"; } return 0; }
E 网络流,最大权闭合子图
一眼最大权闭合子图板子,具体做法和证明见这题:P2762 太空飞行计划问题。
看不出来的鉴定为没写完网络流 24 题。
最搞的是调了二十分钟发现自己板子写错了哈哈。
//知识点:网络最大流,Dinic /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 1e5 + 10; const int kM = 2e6 + 10; const LL kInf = 1e18 + 2077; //============================================================= int n, m, k, S, T; int nodenum, maxnodenum, edgenum = 1, v[kM], ne[kM], head[kN]; int cur[kN], dep[kN]; LL w[kM]; //============================================================= void addedge(int u_, int v_, LL w_) { v[++ edgenum] = v_; w[edgenum] = w_; ne[edgenum] = head[u_]; head[u_] = edgenum; v[++ edgenum] = u_; w[edgenum] = 0; ne[edgenum] = head[v_]; head[v_] = edgenum; } void init() { std::cin >> n; edgenum = 1; nodenum = n + 61; maxnodenum = n + 100; S = ++ nodenum, T = ++ nodenum; for (int i = 1; i <= maxnodenum; ++ i) { head[i] = 0; } for (int i = 0; i < 60; ++ i) addedge(n + i + 1, T, 1); for (int i = 1; i <= n; ++ i) { LL a; std::cin >> a; addedge(S, i, 1); for (LL j = 0; j < 60; ++ j) { if (a >> j & 1ll) addedge(i, n + j + 1, kInf); } } } bool bfs() { std::queue <int> q; memset(dep, 0, (nodenum + 1) * sizeof (int)); dep[S] = 1; //注意初始化 q.push(S); while (!q.empty()) { int u_ = q.front(); q.pop(); for (int i = head[u_]; i > 1; i = ne[i]) { int v_ = v[i]; LL w_ = w[i]; if (w_ > 0 && !dep[v_]) { dep[v_] = dep[u_] + 1; q.push(v_); } } } return dep[T]; } LL dfs1(int u_, LL into_) { if (u_ == T) return into_; LL ret = 0; for (int i = cur[u_]; i > 1 && into_; i = ne[i]) { int v_ = v[i]; LL w_ = w[i]; if (w_ && dep[v_] == dep[u_] + 1) { LL dist = dfs1(v_, std::min(into_, w_)); if (!dist) dep[v_] = kN; into_ -= dist; ret += dist; w[i] -= dist, w[i ^ 1] += dist; if (!into_) return ret; } } if (!ret) dep[u_] = 0; return ret; } LL dinic() { LL ret = 0; while (bfs()) { memcpy(cur, head, (nodenum + 1) * sizeof (int)); ret += dfs1(S, kInf); } return ret; } //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { init(); std::cout << n - dinic() << "\n"; } return 0; }
F
写在最后
zzz。
败犬女主真好看。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】