Educational Codeforces Round 169 (Rated for Div. 2)
写在前面
比赛地址:https://codeforces.com/contest/2004
又是手速场,最后把 F 口了没时间写了。小号又能上紫了爽,要是手快点还能更爽。
置顶广告:中南大学 ACM 集训队绝赞招新中!
有信息奥赛基础,获得 NOIP 省一等奖并达到 Codeforces rating 1900+ 或同等水平及以上者,可以直接私聊我与校队队长联系,免选拔直接进校集训队参加区域赛!
没有达到该水平但有志于 XPCX 赛事请关注每学年开始的 ACM 校队招新喵!
到这个时候了还缺队友实在不妙!求求求求快来个大神带我呜呜呜呜
A
签到。
数据范围很小,懒得多想了于是直接枚举插入点的位置检查。
复制复制// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long //============================================================= //============================================================= //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { int n; std::cin >> n; std::vector<int> a(n + 1), b; for (int i = 1; i <= n; ++ i) { std::cin >> a[i]; if (i >= 2) b.push_back(a[i] - a[i - 1]); } // std::sort(b.begin(), b.end()); int fl = 0; for (int i = -100; i <= 200; ++ i) { int maxd = 0; for (int j = 1; j <= n; ++ j) { maxd = std::max(maxd, abs(i - a[j])); } int flag = 1; for (auto d: b) if (maxd >= d) flag = 0; if (flag == 1) { fl = 1; break; } } std::cout << (fl ? "YES\n" : "NO\n"); } return 0; }
B
结论,特判。
恶心特判题,我写了一坨。
特别注意端点重合的情况!
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long //============================================================= //============================================================= //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { int l, r, L, R, ans; std::cin >> l >> r >> L >> R; if (l > R || L > r) { ans = 1; } else if (l == L && r == R) { ans = R - L; } else if (l <= L && R <= r && (l == L || R == r)) { ans = R - L + 1; } else if (L <= l && r <= R && (l == L || R == r)) { ans = r - l + 1; } else if (l < L && R < r) { ans = R - L + 2; } else if (L < l && r < R) { ans = r - l + 2; } else if (l <= L) { ans = r - L + 2; } else { ans = R - l + 2; } std::cout << ans << "\n"; } return 0; } /* 1 1 2 2 3 */
C
贪心,排序
先不考虑风灵月影宗师 Bob 企图开挂的行为,发现在一局游戏中两人一定是将 按照权值递减排序,并交替地选择此时的最大值。
然后考虑 Bob 的修改。显然他应当保证他修改后增加的权值一定全部被自己拿到,则应当保证修改后各个权值降序排序后相对位置不发生改变。显然此时他的最优操作是对于降序排序后的数列 ,对于他可以取到的所有 的位置 ,都尝试将每个 修改为 。于是直接模拟即可。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long //============================================================= //============================================================= //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { int n, k; std::cin >> n >> k; LL ans = 0; std::vector<int> a(n); for (int i = 0; i < n; ++ i) std::cin >> a[i]; std::sort(a.begin(), a.end(), std::greater<int>()); for (int i = 0; i < n; ++ i) { if (i % 2 == 0) ans += a[i]; else { int delta = std::min(k, a[i - 1] - a[i]); a[i] += delta, k -= delta; ans -= a[i]; } } std::cout << ans << "\n"; } return 0; }
D
枚举,结论
发现在大多数情况下给定的询问 均可以直达,此时的代价即为 。
无法直达的情况 所在城市的种类只有三种:BG
和 RY
,BR
和 GY
,BY
和 GR
。容易发现对于这三种情况,仅需找到任意一个类型与它们均不同的城市 ,就可以作为中转点使得 可互相到达,代价即为 。
若中转城市恰好位于 中则代价即为 ,否则仅需检查距离 最近的中转城市即可。于是考虑按照位置升序维护每种城市的位置,对于每次询问先判断 是否可以直达,若无法直达则枚举中转城市的类型,检查 中有无中转城市,若没有则在 中二分找到最优的中转城市即可。
总时间复杂度 级别。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 2e5 + 10; const int kInf = 1e9; //============================================================= int n, q, t[kN], cnt[7][kN]; std::map<std::string, int> type; std::vector<int> pos[7]; //============================================================= int oppo(int x_) { return (x_ <= 3 ? x_ + 3 : x_ - 3); } //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); //BG, BR, BY, GR, GY, or RY type["BG"] = 1; type["BR"] = 2; type["BY"] = 3; type["RY"] = 4; type["GY"] = 5; type["GR"] = 6; int T; std::cin >> T; while (T --) { std::cin >> n >> q; for (int i = 1; i <= 6; ++ i) pos[i].clear(); for (int i = 1; i <= n; ++ i) { std::string s; std::cin >> s; t[i] = type[s]; pos[t[i]].push_back(i); for (int j = 1; j <= 6; ++ j) cnt[j][i] = cnt[j][i - 1]; cnt[t[i]][i] ++; } while (q --) { int x, y; std::cin >> x >> y; if (x > y) std::swap(x, y); if (oppo(t[x]) != t[y]) { std::cout << abs(x - y) << "\n"; continue; } int ans = kInf; for (int i = 1; i <= 6; ++ i) { if (i == t[x] || i == t[y]) continue; if (cnt[i][y - 1] - cnt[i][x] != 0) ans = y - x; if (cnt[i][x - 1]) { int p = std::lower_bound(pos[i].begin(), pos[i].end(), x) - pos[i].begin(); -- p; ans = std::min(ans, x - pos[i][p] + y - pos[i][p]); } if (cnt[i][n] - cnt[i][y]) { int p = std::lower_bound(pos[i].begin(), pos[i].end(), y) - pos[i].begin(); ans = std::min(ans, pos[i][p] - y + pos[i][p] - x); } } if (ans >= kInf) ans = -1; std::cout << ans << "\n"; } } return 0; }
E
博弈论,SG 函数
比较裸的 SG 函数问题,妈的标题不是 Not a Nim Problem 吗。
手玩下发现,对于单堆石子当且仅当个数 为奇数时有先手必胜策略,此时仅需一步取走 个石子即可。然后考虑通过对 求 SG 函数拓展到多堆石子即可。
直接大力枚举转移求 SG 函数显然不行,互质对的数量级会很大。但是发现这个数据范围非常像是给线性筛跑的,于是考虑寻找每个值与其最小质因子的关系。手玩下发现:
- ,。
- 对于所有偶数 ,。
- 对于所有大于 2 的质数 ,有 ,则 ,其中 代表 在所有质数(包括 2)中的排名。
- 对于所有奇数 ,设其最小质因子为 ,发现有 ,且 。则 第一个不能转移到的位置是 。通过观察转移对象可知有 。
上述规律赛时是大力手玩到 10 发现的,如果打表的话可以更快发现规律。
于是仅需通过线性筛,对于每个数求得其最小质因子,即可求得所有 函数,则仅需判断 是否成立即可。
总时间复杂度 级别。
// /* By:Luckyblock https://www.luogu.com.cn/problem/P3383 */ #include <bits/stdc++.h> #define LL long long const int kN = 1e7 +10; //============================================================= int n, q; bool vis[kN]; int pnum, p[kN], sg[kN]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void Getprime() { n = 1e7; sg[0] = 0, sg[1] = 1; for (int i = 2; i <= n; ++ i) { if (i % 2 == 0) sg[i] = 0; if (!vis[i]) { p[++ pnum] = i, sg[i] = pnum; if (i == 2) sg[i] = 0; } for (int j = 1; j <= pnum; ++ j) { if (i * p[j] > n) break; vis[i * p[j]] = true; if (i * p[j] % 2 == 1) sg[i * p[j]] = sg[p[j]]; if (i % p[j] == 0) break; } } } //============================================================= int main() { // freopen("1.txt", "r", stdin); Getprime(); int T = read(); while (T --) { n = read(); int sum = 0; for (int i = 1; i <= n; ++ i) { int a = read(); sum ^= sg[a]; } std::cout << (sum == 0 ? "Bob" : "Alice") << "\n"; } return 0; }
F
结论,模型转化,枚举
最后还剩二十分钟本来想下班了,但是看到群友发的玛丽涩图之后灵光一闪就会了,以后应当在赛时就多看玛丽涩图!
首先对于某个区间 ,操作次数上界即合并为同一个数,次数为 。
如果从回文串等价于两端对应位置相等的角度考虑——会发现题目给定的操作既会影响串的长度又会影响串的元素的值,这很几把麻烦,所以要换个判定回文串的思路:
某个串是回文串的另一个充要条件是:该串从前往后遍历与从后往前遍历对应位置完全相同,这又等价于该串的前缀和与后缀和对应位置完全相同。由于前后缀和一定是单调递增的,发现此时整个值域被前后缀和划分成了若干子区间。对于给定的两种操作:
- 操作 1 将某两个位置合并,可看做选择某个位置 ,并将该位置对应权值 前缀和左侧区间,与后缀和右侧区间的一个权值删除。
- 操作 2 将某个位置分裂,可以看做选择某个位置 ,在该位置对应权值 前缀和右侧区间,与后缀和左侧区间里分别加一个值并满足 的限制。
例如:

发现此时通过操作将区间转化为回文串,即通过上述增删权值的操作,使得前后缀和对应位置完全相同。手玩下发现此时需要的次数实际上即操作次数上界,减去未操作时,前后缀和里相等的权值的个数。
于是考虑先记答案为所有区间操作次数的上界 ,然后考虑减去每个区间的前后缀和中权值相等的个数即可。此时可以通过维护每种权值出现的次数,分别考虑前缀和与后缀和即可。具体实现详见以下江队的提交:https://codeforces.com/contest/2004/submission/276668467。
总时间复杂度 级别。
#include<bits/stdc++.h> #define ll long long //#define int long long #define mid ((l+r)>>1) #define lson u<<1,l,mid #define rson u<<1|1,mid+1,r #define inf 0x3f3f3f3f #define pii std::pair<int,int> typedef int LL; const signed maxn=(signed)2e3+5; inline LL Read(){ char ch=getchar();bool f=0;LL x=0; for(;!isdigit(ch);ch=getchar())if(ch=='-')f=1; for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48); if(f==1)x=-x;return x; } int ar[maxn],pre[maxn]; int ur[maxn*maxn],tot; int all[maxn*maxn]; int idx[maxn][maxn]; signed main() { LL T=Read(); while (T--){ int n=Read(); for(int i=1;i<=n;++i) ar[i]=Read(); pre[0]=0; for(int i=1;i<=n;++i) pre[i]=pre[i-1]+ar[i]; tot=0; for(int i=1;i<=n;++i){ for(int j=i;j<=n;++j){ ur[++tot]=pre[j]-pre[i-1]; } } std::sort(ur+1,ur+1+tot); int cnt=std::unique(ur+1,ur+1+tot)-ur-1; for(int i=0;i<=cnt;++i) all[i]=0; for(int i=1;i<=n;++i){ for(int j=i;j<=n;++j){ idx[i][j]=std::lower_bound(ur+1,ur+1+cnt,pre[j]-pre[i-1])-ur; ++all[idx[i][j]]; //printf("idx[%d][%d]=%d\n",i,j,idx[i][j]); } } ll sum=1ll*n*(n*n-1)/6; //printf("%lld\n",sum); for(int i=1;i<=n;++i){ for(int j=i;j<=n;++j) --all[idx[i][j]]; for(int j=i;j<=n;++j) sum-=all[idx[i][j]]; //printf("%lld\n",sum); } printf("%lld\n",sum); } return 0; }
写在最后
学到了什么:
- E:观察 sg 函数的转移形式,或打表大眼观察 sg 函数的规律。
- F:回文串的多个充要条件。
唉最近忙着准备暑假结训赛没什么时间补题,欠了两场牛客一场 div1+2 没补好爽。
我们人手十分不足的弱校是这样的唉,现在处于一个又想各位牛逼 OI 大神来报考中南大专再度出线 WF 重铸 ACM 辉煌,又不想大家因为各种原因受苦的矛盾心理状态。希望从今年招新开始修补一下吧,希望到时候会有人记得当年有一个叫 Lb 的大傻逼在这里待过。
结尾广告:中南大学 ACM 集训队绝赞招新中!
有信息奥赛基础,获得 NOIP 省一等奖并达到 Codeforces rating 1900+ 或同等水平及以上者,可以直接私聊我与校队队长联系,免选拔直接进校集训队参加区域赛!
没有达到该水平但有志于 XPCX 赛事请关注每学年开始的 ACM 校队招新喵!
到这个时候了还缺队友实在不妙!求求求求快来个大神带我呜呜呜呜
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】