第九届中国大学生程序设计竞赛总决赛(CCPC Final 2023)

news/2025/2/21 9:10:53/文章来源:https://www.cnblogs.com/cjjsb/p/18724254

Preface

入土了快三个月突然诈尸了

之前停更的主要原因还是因为去年的 ICPC 昆明再打一银后,再加上发生了许多事情,导致整个人完全没有继续打算竞的动力了遂直接一摆到底了

但不管怎么说下个月的 CCPC Final 还是要参加的,估计大概率是个人算竞生涯的最后一舞了,因此近期会低频率地 VP 一下来保持下那可怜的手感

这场去年的决赛由于是我们学校承办的,当时作为志愿者参加了,因此或多或少对题目都有了些了解,导致相比正式参赛选手有了不小的 BUFF

最后 6 题低罚时还有金尾,只能说全是队友秒了 AC 的功劳,和我关系不大


A. Add One 2

题都没看是啥,我在 J 题坐牢的时候队友就会了,好像是个差分+贪心来着,后面发现这题现场才过了 10 个队左右,大为震惊

#include <bits/stdc++.h>using llsi = long long signed int;
constexpr int MN = 1'000'000'000;void work() {int n; std::cin >> n;std::vector<int> c(n + 2);c[0] = c[n + 1] = MN;llsi c_sum = MN;for(int i = 1; i <= n; ++i) {std::cin >> c[i];c_sum += c[i];}n += 1;for(int i = n; i >= 1; --i)c[i] -= c[i - 1];llsi neg = 0, cost = 0;for(int i = 1; i <= n; ++i) if(c[i] < 0) neg -= c[i];std::vector<int> pre(n + 2), nxt(n + 2), erased(n + 2, 0); for(int i = 1; i <= n + 1; ++i) pre[i] = i - 1;for(int i = 0; i <= n; ++i)     nxt[i] = i + 1;auto erase = [&](int a) {nxt[pre[a]] = nxt[a];pre[nxt[a]] = pre[a];erased[a] = 1;};using node = std::pair<int, int>;std::priority_queue<node, std::vector<node>, std::greater<node>> pq;auto update = [&](int i) {if(erased[i] || i == 0 || i == n + 1) return ;if(c[i] >= 0) return ;if(nxt[i] == n + 1) return ;if(c[nxt[i]] <= 0) return ;pq.push({ nxt[i] - i, i });};for(int i = 1; i <= n; ++i) if(c[i] == 0) erase(i);for(int i = 1; i <= n; ++i) update(i);while(neg > MN) {assert(pq.size());auto [l, p] = pq.top(); pq.pop();if(erased[p]) continue;if(nxt[p] - p != l) continue;int nx = nxt[p];int upt = std::min({llsi(-c[p]), llsi(c[nx]), neg - MN});cost += llsi(l) * upt;neg -= upt, c[p] += upt, c[nx] -= upt;int d = pre[p], e = nxt[nx];if(c[p] == 0) erase(p); if(c[nx] == 0) erase(nx);update(d), update(p), update(nx), update(e);}std::cout << c_sum + cost - MN << char(10);return ;
}int main() {std::ios::sync_with_stdio(false);int T; std::cin >> T; while(T--) work();return 0;
}

C. Colorful Graph 2

这就是场外剧透的力量,那个 BFS 染色的方法只能说空想确实难想到,但见过了就直接秒了

#include <bits/stdc++.h>int main() {int T; std::cin >> T; while(T--) {int n, m; std::cin >> n >> m;std::vector<std::vector<int>> out(n);std::vector<int> dis(n, 0x7f7f7f7f);for(int i = 1; i < n; ++i) out[i].push_back(i - 1), out[i - 1].push_back(i);out[0].push_back(n - 1); out[n - 1].push_back(0);while(m--) {int f, t;std::cin >> f >> t;out[f].push_back(t); out[t].push_back(f);}dis[0] = 0;std::queue<int> q;q.push(0);while(q.size()) {int hd = q.front(); q.pop();for(auto out: out[hd]) if(dis[out] > dis[hd] + 1) {dis[out] = dis[hd] + 1;q.push(out);}}for(int i = 0; i < n; ++i) std::cout << (dis[i] % 2 == 0 ? 'R' : 'B');std::cout << char(10);}return 0;
}

D. Min or Max

签到,由于题意等价于可以删去任意位置上的数,因此只要判 \(\{b\}\) 是不是 \(\{a\}\) 的子序列即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,m,a[N],b[N];
int main()
{for (scanf("%d",&t);t;--t){scanf("%d%d",&n,&m);for (RI i=1;i<=n;++i) scanf("%d",&a[i]);for (RI i=1;i<=m;++i) scanf("%d",&b[i]);bool flag=1; for (RI i=1,j=1;i<=m;++i){while (j<=n&&a[j]!=b[i]) ++j;if (j>n) { flag=0; break; } else ++j;}puts(flag?"Yes":"No");}return 0;
}

F. Whose Land?

这题赛事基本想出做法了,就是先离线用扫描线处理右端点,然后加入一个点的影响可以用 BFS 序来做到最多影响 \(O(k)\) 级别的区间,然后就是用数据结构乱搞搞就行了

代码懒得写了,估计太久没写码量题也写不动了


G. China Convex Polygon Contest

读懂题意后会发现其实就是给出了若干个区间 \([l_i,r_i)\),在这个区间内 \(x\) 时间交题的话贡献为 \(r_i-x\)

不难发现把 \(\{b\}\) 从小到大排序的做题顺序一定最优,求出前缀和后就得到了若干个题目完成的节点 \(s_j\)

对于每个时间节点可以选择两种操作:做完立刻提交;或者留到后面某个区间的左端点时刻再提交

不难发现这是个很经典的带悔贪心的模型,考虑从后往前考虑每个区间 \([l_i,r_i)\),并用一个大根堆来维护反悔选项

对于该区间内的所有节点 \(s_j\),除了最靠前的那个显然都是选择往后移动最优,直接取出堆顶即可

对于最靠前的那个节点,比较往后移动和保留二者的贡献,若选择保留则把 \(s_j-l_i\) 加在堆顶的值上,以表示前面的移动选项可以通过放在 \(l_i\) 并移动 \(s_j\) 来得到更优的策略

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,m,a[N];
signed main()
{for (scanf("%lld",&t);t;--t){scanf("%lld%lld",&n,&m); vector <int> b(n);for (RI i=1;i<=n;++i) scanf("%d",&a[i]);for (RI i=0;i<n;++i) scanf("%d",&b[i]);a[0]=0; a[n+1]=m; sort(b.begin(),b.end());for (RI i=1;i<n;++i) b[i]+=b[i-1];// for (auto x:b) printf("%lld ",x); putchar('\n');priority_queue <int> hp; int ans=0;while (!b.empty()&&b.back()>m) b.pop_back();for (RI i=n;i>=0;--i){int cnt=0,mn=m;while (!b.empty()&&b.back()>=a[i]){++cnt; mn=b.back(); b.pop_back();}if (cnt==0){hp.push(a[i+1]-a[i]); continue;}int token=min((int)hp.size(),cnt-1);for (RI j=1;j<=token;++j) ans+=hp.top(),hp.pop();int x=a[i+1]-mn,y=(hp.empty()?0:hp.top());// printf("i = %lld, x = %lld, y = %lld\n",i,x,y);if (x>=y){ans+=x;if (hp.empty()) hp.push(mn-a[i]);else hp.pop(),hp.push(y+mn-a[i]);} else{ans+=y; hp.pop();hp.push(a[i+1]-a[i]);}// printf("ans = %lld\n",ans);}printf("%lld\n",ans);}return 0;
}

H. The Game

稍作思考会发现本质就是看最后时刻剩下两个元素时会不会相同即可

以此推出先手的策略就是拿出现次数最多的元素;后手的策略就是拿出现次数最少的元素

推一下式子会发现最后就是判断出现的数的种类数和 \(n\) 的大小关系

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=2000005;
int t,n,x,vis[N];
int main()
{for (scanf("%d",&t);t;--t){scanf("%d",&n);for (RI i=1;i<=2*n;++i) vis[i]=0;for (RI i=1;i<=2*n;++i) scanf("%d",&x),vis[x]=1;int cnt=0; for (RI i=1;i<=2*n;++i) cnt+=vis[i];puts(cnt<=n?"Qingyu":"Kevin");}return 0;
}

J. DFS Order 5

写完第一版 WA 了后手玩了 1h 没找到反例,最后还是靠队友 fix 了个 Corner Case 才过

考虑对于序列中相邻的两个元素 \(x,y\),要检验能否在 \(x\) 后面接上 \(y\),分类讨论下:

  • 如果 \(y\) 是之前任意出现过的元素的祖先,则一定无解;
  • 如果 \(y\)\(x\) 的儿子,则直接接上即可;
  • 如果 \(\operatorname{LCA}(x,y)\) 不是 \(y\) 的父亲,则一定无解,因为此时相当于会跳过没出现的元素;

经过检验后,剩下的情况只有\(\operatorname{LCA}(x,y)\)\(y\) 的父亲的情形,此时我们需要从 \(x\) 开始向上跳逐个检验:

  • 若该点之前没在 DFS 序中出现过,则通过检验;
  • 否则该点的子树内的所有点都必须在 DFS 序中出现过;

知道到达 \(\operatorname{LCA}(x,y)\) 为止,用树状数组很容易动态维护子树内点数

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,q,x,y,vis[N],idx,fa[N],L[N],R[N]; vector <int> v[N];
inline void DFS(CI now=1,CI anc=0)
{L[now]=++idx; fa[now]=anc;for (auto to:v[now]) if (to!=anc) DFS(to,now);R[now]=idx;
}
class Tree_Array
{private:int bit[N];public:#define lowbit(x) (x&-x)inline void add(RI x,CI y){for (;x<=n;x+=lowbit(x)) bit[x]+=y;}inline int get(RI x,int ret=0){for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;}#undef lowbit
}BIT;
int main()
{scanf("%d%d",&n,&q);for (RI i=1;i<n;++i)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);for (DFS();q;--q){int m; scanf("%d",&m); bool flag=1;vector <int> vec(m),rcv;for (RI i=0;i<m;++i) scanf("%d",&vec[i]),++vis[vec[i]];for (RI i=0;i<m;++i) if (vis[vec[i]]>1) { flag=0; break; }if (!flag){puts("No");for (RI i=0;i<m;++i) vis[vec[i]]=0;continue;}if (vis[1]&&vec[0]!=1){puts("No");for (RI i=0;i<m;++i) vis[vec[i]]=0;continue;}for (RI i=0;i<m;++i) vis[vec[i]]=0;rcv.push_back(vec[0]); vis[vec[0]]=1; BIT.add(L[vec[0]],1);for (RI i=0;i+1<m&&flag;++i){int x=vec[i],y=vec[i+1];if (BIT.get(R[y])-BIT.get(L[y]-1)!=0) { flag=0; break; }                                                                          if (fa[y]==x) { rcv.push_back(y); vis[y]=1; BIT.add(L[y],1); continue; }if (L[y]<=L[x]&&L[x]<=R[y]) { flag=0; break; }if (L[fa[y]]<=L[x]&&L[x]<=R[fa[y]]){// printf("check x=%d\n",x);for (;x!=fa[y];x=fa[x]){if (!vis[x]) break;if (BIT.get(R[x])-BIT.get(L[x]-1)!=R[x]-L[x]+1) { flag=0; break; }}if (flag) rcv.push_back(y),vis[y]=1,BIT.add(L[y],1);} else { flag=0; break; }}for (auto x:rcv) vis[x]=0,BIT.add(L[x],-1);puts(flag?"Yes":"No");}return 0;
}

Postscript

久违地写了会代码发现手速啥的好像还没有退化太多,或许是个好迹象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/886726.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为DeepSeek添加本地知识库

为什么要尝试给DeepSeek添加本地知识库呢?作为一个程序员,以前也用过很多AI产品,直到春节DeepSeek爆火,成功在自己的电脑上把AI模型跑起来的时候才真正感受到AI已近在咫尺。未来很多应用和项目都离不开这个工具。或许在我们OA系统中会出现一个AI小助手,我们会问它差旅费报…

SciTech-EECS-Circuits-Digital(数字电路): Latch(锁存器) + Flip/Flop(触发器) + 记忆存储电路

SciTech-EECS-Circuits-Digital(数字电路) Latch(锁存器)Flip/Flop(触发器)记忆存储电路

山石网科阿里云通义灵码,开启研发“AI智造”新时代

近日,山石网科正式宣布全面接入阿里云通义灵码企业专属版,这标志着山石网科在研发智能化、自动化领域迈出重要一步,为研发工作注入强大的AI动力,实现多维度的效率飞跃。近日,山石网科正式宣布全面接入阿里云通义灵码企业专属版,这标志着山石网科在研发智能化、自动化领域…

frame/tab 切换,一些技巧

点击这里,边看视频讲解,边学习以下内容 frame切换 请大家点击这里,打开这个链接 如果我们要 选择 下图方框中 所有的 蔬菜,使用css选择,怎么写表达式? 当然,要先查看到它们的html元素特征大家可能会照旧写出如下代码:from playwright.sync_api import sync_playwrightp…

CSS选择器 定位方法

定位元素的重要性 前面这段代码from playwright.sync_api import sync_playwrightp = sync_playwright().start() browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto("https://www.byhy.net/cdn2/files/selenium/stock1.html")# 输入…

Arduino-Esp8266 OTA升级

#include <ESP8266WiFi.h> #include <ESP8266httpUpdate.h> #include <Ticker.h>/******需要修改的地方****************/#define wifi_name "GT-2G" //WIFI名称,区分大小写,不要写错 #define wifi_password "gt#@10000" …

【IDEA】idea接入AutoDev插件并配置DeepSeek

1. AutoDev Quick Start https://ide.unitmesh.cc/quick-start2. AutoDev 下载 https://github.com/unit-mesh/auto-dev/releases3. 根据IDEA版本进行下载 比如我的IDEA版本是2024.3.2.1 那么我应该下载的就是241.zip 需要根据最新的quick-start去release下载对应版本 4. IDEA…

CF559E Gerald and Path 题解

CF559E Gerald and Path 很困难的 DP 题,状态不是很好想。对于这种线段覆盖类题目,显然先覆盖哪个线段没有影响,我们可以通过按照端点位置升序排序后按照顺序考虑,这样可能会有一些额外性质。 之后,考虑转移时需要什么东西来刻画一个状态的轮廓。显然我们需要知道现在是第…

中国PostgreSQL数据库认证体系和学习方向

中国PostgreSQL数据库认证体系和学习方向PostgreSQL认证,指的是PostgreSQL数据库管理员的能力认证,用来判断从业人员是否具备管理和维护PostgreSQL数据库的能力,由于数据库中存放着很多重要的数据,所以对于从业人员的要求极高,所以企业在招聘相关岗位的时候,会优先考虑有…

ATTCK实战系列(一)

环境下载 下载靶场环境,并导入虚拟机分别是win2003、win7、winserver2008配置网络 虚拟机——编辑——虚拟机网络编辑器——添加网络VMnet2——仅主机模式分配的地址是192.168.52.0配置好地址后,需要将win2003和winserver2008的网卡设置为VMnet2win7的网络需要两张,一张为VM…

DHTMLX Gantt 甘特图导出全数据图/PDF

最近有个需求,将项目甘特图导出图片,但发现问题:当项目甘特图内的行数很多时(这是必然,当项目周期长,建立的任务很多,就会出现很多任务行),超过了甘特图的可视区域,就会出现滚动条,导出图片的时候只会导出可视区域内显示的甘特图,其他非可视区域的内容没导出(就是…