[Codeforces]Educational Codeforces Round 85 赛后总结
Before the Beginning
转载请将本段放在文章开头显眼处,如有二次创作请标明。
原文链接:https://www.codein.icu/cfedu85/
前言
好久没有打比赛了,周五考完文化课之后看到有深夜场,想了想就打了。
最后只做出来4题,排名一千左右。
在此写一个总结,如有必要也会发布单题题解。咕咕咕
A. Level Statistics
签到题,有点小坑。
题意
一个人在玩游戏,给出若干个状态,以 来表示他现在进行了 次游戏,通关了 次。
解法
很显然,对于每个状态,有如下限制:
- ,因为通关次数不可能比游戏次数还多。
- ,相对于上次的状态,增加的通关次数不可能比增加的游戏次数多。
- ,时间顺序,无需多说。
有一个坑点就是状态是按时间顺序给出的。只有愚蠢的笔者才会看不懂描述跳这个坑吧
所以并不需要按通关次数等等进行排序。
Code
给出比赛时代码。
#include <cstdio> #include <algorithm> using namespace std; template<typename T> void read(T &r) { static char c;r=0; for(c=getchar();c>'9'||c<'0';c=getchar()); for(;c>='0'&&c<='9';r=(r<<1)+(r<<3)+(c^48),c=getchar()); } const int maxn = 110; int T; int n; struct node { int p,c; }a[maxn]; bool cmp(const node &a,const node &b) { if(a.c == b.c) return a.p < b.p; return a.c < b.c; } int main() { read(T); while(T--) { read(n); for(int i = 1;i<=n;++i) read(a[i].p),read(a[i].c); for(int i = 1;i<=n;++i) { if(a[i].c > a[i].p) { puts("NO"); goto end; } if(a[i].c < a[i-1].c) { puts("NO"); goto end; } if(a[i].p < a[i-1].p) { puts("NO"); goto end; } if(a[i].c - a[i-1].c > a[i].p - a[i-1].p) { puts("NO"); goto end; } } puts("YES"); end:; } return 0; }
B. Middle Class
水题,没啥难度。
题意
给出一堆人,可以从中选出一部分人,将其总财富均分。
定义财富多于 的为中产阶级。
问最多有多少中产阶级。
解法
显然要劫富济贫,而总财富需求与人数线性相关。
于是按财富排降序。
计算出假定前 个人都是中产所需的总财富。
计算出当前总财富。
两者相比较,如果当前总财富可以满足则继续向下枚举。
Code
给出比赛时代码。
#include <cstdio> #include <algorithm> using namespace std; template<typename T> void read(T &r) { static char c;r=0; for(c=getchar();c>'9'||c<'0';c=getchar()); for(;c>='0'&&c<='9';r=(r<<1)+(r<<3)+(c^48),c=getchar()); } const int maxn = 1e5 + 10; int T,n,x; int a[maxn]; bool cmp(int a,int b) { return a>b; } int main() { read(T); while(T--) { read(n); read(x); for(int i = 1;i<=n;++i) read(a[i]); sort(a+1,a+1+n); long long now = 0; long long need = 0; int ans = 0; for(int i = n;i;--i) { now += a[i]; need += x; if(now < need) { printf("%d\n",ans); goto end; } ++ans; } if(ans == n) printf("%d\n",ans); else if(ans == 0) puts("0"); end:; } return 0; }
CF1334C Circle of Monsters
思考一会可以得到结论的贪心题。
题意
给出一圈怪兽,每个怪兽有两个属性 ,分别代表生命值和死亡后对下一个怪兽的伤害值。
要求最少造成多少伤害就可以杀光怪兽。
解法
首先有一个显然的结论:按顺序杀一定最优。
因为每个怪兽所受到伤害只有两个来源:
- 直接造成伤害
- 前一个怪兽死亡造成伤害
而开始造成伤害后,因为这只怪兽一定要死,那么肯定是先杀前面一个能更省力。
按顺序杀光怪兽的话,枚举开始点,直接模拟显然是一种可行的做法。
但模拟复杂度 无法通过,可以用预处理降复杂度。
设 为前一只怪兽死后,这只怪兽还剩余的血量。
有 ,边界特殊处理一下即可。
那么求出 的总和,记为 。
假定现在从第 只怪兽开始杀,那么:
从 中减去 ,再加上 ,即为当前情况的总花费伤害。
复杂度降低到 ,可以通过。
Code
给出比赛时的代码。
#include <cstdio> using namespace std; template<typename T> inline T _max(const T &a,const T &b){return a>b?a:b;} template<typename T> void read(T &r) { static char c;r=0; for(c=getchar();c>'9'||c<'0';c=getchar()); for(;c>='0'&&c<='9';r=(r<<1)+(r<<3)+(c^48),c=getchar()); } const int maxn = 3e5 + 10; int T,n; long long a[maxn],b[maxn],c[maxn]; int main() { read(T); while(T--) { read(n); for(int i = 1;i<=n;++i) read(a[i]),read(b[i]); long long sum = 0; for(int i = 2;i<=n;++i) sum += (c[i] = _max(a[i] - b[i-1],0ll)); sum += (c[1] = _max(a[1] - b[n],0ll)); int minn = 1; long long ans = 1ll << 60; for(int i = 1;i<=n;++i) { long long ret = sum - c[i] + a[i]; if(ret < ans) ans = ret; } printf("%lld\n",ans); } return 0; }
D. Minimum Euler Cycle
依然是贪心的题目。但笔者将其做成了找规律……
题意
给出一张包含 个顶点的完全图。
要求出一条路径,使得每条边都经过恰好一次。(单向边)
并且要求经过顶点字典序最小。
解法
笔者先从贪心入手。
很显然从顶点 出发,去往顶点 。
此时可以回到顶点 使得字典序最小,也可以选择去往其他顶点。
此时笔者考虑到回到顶点 后可能无法完成图的遍历,因此暂时搁置了这个想法。
由于数据范围大,笔者猜想一定有规律可以快速求解。
所以笔者写了个 的暴力来打表。
#include <cstdio> #include <cstring> using namespace std; const int maxn = 50; int n; int vis[maxn][maxn]; int path[maxn]; int temp[maxn]; int maxt; void dfs(int u,int t) { temp[t] = u; maxt = t>maxt?t:maxt; bool over = true; for(int i = 1;i<=n;++i) for(int j = 1;j<=n;++j) if(i != j && !vis[i][j]) over = false; if(over) { for(int i = 1;i<=maxt;++i) if(temp[i] < path[i]) { for(int j = 1;j<=maxt;++j) path[j] = temp[j]; break; } else if(temp[i] > path[i]) break; return; } for(int i = 1;i<=n;++i) if(i != u && !vis[u][i]) { vis[u][i] = 1; dfs(i,t+1); vis[u][i] = 0; } } int main() { memset(path,0x3f,sizeof(path)); scanf("%d",&n); dfs(1,1); for(int i = 1;i<=maxt;++i) printf("%d ",path[i]); return 0; }
打表后发现,字典序最小的路径如下形式:
n = 2: 1 2 1 n = 3: 1 2 1 3 2 3 1 n = 4: 1 2 1 3 1 4 2 3 2 4 3 4 1 ......
找到规律了,此时可以结合之前的贪心思路:
由顶点 访问到其他点后,再回到顶点 可以使得字典序最小。
依次访问到最后一点后,可以去往顶点 使得字典序最小。
然后如法炮制,不断访问……
最后从最后的顶点再回到顶点 。
刚好全部遍历一遍。
Code
代码实现上,笔者运用了前文的像金字塔一样的规律。
预处理出每层的节点数量,以判断所求左右端点在哪一层。
随后依次输出即可。
层内规律:
设节点在第 层的第 个节点,那么 为奇数时,输出 ,否则输出 。
最后一点是 ,特判一下即可。
#include <cstdio> using namespace std; template<typename T> void read(T &r) { static char c;r=0; for(c=getchar();c>'9'||c<'0';c=getchar()); for(;c>='0'&&c<='9';r=(r<<1)+(r<<3)+(c^48),c=getchar()); } const int maxn = 1e5 + 10; int T; long long n,l,r; long long s[maxn]; long long f; int main() { read(T); while(T--) { bool flag = false; long long lp = 1,rp = 1; long long lp2; long long rp2; read(n); f = n * (n-1) + 1; read(l); read(r); for(int i = 1;i<=n;++i) s[i] = ((n-i) << 1) + s[i-1]; if(l == s[n]+1) { flag = true; goto end; } if(r == s[n]+1) { flag = true; --r; } while(s[lp] < l) ++lp; while(s[rp] < r) ++rp; lp2 = l - s[lp-1]; rp2 = r - s[rp-1]; if(lp == rp) { for(int i = lp2;i<=rp2;++i) { if(i & 1) printf("%lld ",lp); else printf("%lld ",lp + (i>>1)); } goto end; } for(long long i = lp2;i<=s[lp] - s[lp-1];++i) { if(i & 1) printf("%lld ",lp); else printf("%lld ",lp + (i>>1)); } for(long long i = lp+1;i<rp;++i) { for(long long p = 1;p<=s[i] - s[i-1];++p) { if(p & 1) printf("%lld ",i); else printf("%lld ",i + (p>>1)); } } for(long long i = 1;i<=rp2;++i) { if(i & 1) printf("%ld ",rp); else printf("%lld ",rp + (i >> 1)); } end: if(flag) printf("1"); printf("\n"); } return 0; }
End
打完 D 后只剩下半小时,笔者就放弃去睡觉了。
后续题目笔者可能会补题,有必要的会单独发题解。
咕咕咕
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 开发者新选择:用DeepSeek实现Cursor级智能编程的免费方案
· Tinyfox 发生重大改版
· 独立开发经验谈:如何通过 Docker 让潜在客户快速体验你的系统
· 小米CR6606,CR6608,CR6609 启用SSH和刷入OpenWRT 23.05.5
· 近期最值得关注的AI技术报告与Agent综述!