UNIQUE VISION Programming Contest 2023 Christmas (AtCoder Beginner Contest 334) A~G 题解

A - Christmas Present

题目大意

给定两个正整数 \(B,G\)\(1\le B,G\le 1000\)\(B\ne G\)),判断哪个更大。

分析

模拟即可。

代码

#include <cstdio>
using namespace std;int main()
{int b, g;scanf("%d%d", &b, &g);puts(b > g? "Bat": "Glove");return 0;
}

B - Christmas Trees

题目大意

给定 \(A,M,L,R\)
对于任意整数 \(k\),Snuke 都会在数轴上的 \(A+kM\) 处放置一棵圣诞树。
试问区间 \([L,R]\) 中共有多少棵圣诞树?

\(-10^{18}\le A\le 10^{18}\)
\(1\le M\le 10^9\)
\(-10^{18}\le L\le R\le 10^{18}\)

分析

不难发现,对于任意整数 \(x\),数轴上 \(x\) 处有圣诞树当且仅当 \(x \equiv A \ (\bmod \ M)\)。变形可得 \(x-A \equiv 0 \ (\bmod \ M)\),即 \((x-A) \mid M\)。故只需考虑相对于 \(A\) 的坐标,所以统计 \([L-A,R-A]\)\(M\) 的倍数数量即可。

代码

使用 C++ 语言时,注意正确处理负数的情况。

#include <cstdio>
using namespace std;using LL = long long;int main()
{LL a, l, r;int m;scanf("%lld%d%lld%lld", &a, &m, &l, &r);l -= a, r -= a;if(l < 0) l = -((-l) / m * m);else l = (l + m - 1) / m * m;if(r < 0) r = -((-r + m - 1) / m * m);else r = r / m * m;printf("%lld\n", (r - l) / m + 1);return 0;
}

C - Socks 2

题目大意

有长为 \(2N\) 的序列 \(S=(1,1,2,2,\dots,N,N)\)
给定 \(A=(A_1,\dots,A_K)\),将 \(S\) 中数字 \(A_1,\dots,A_K\) 各拿掉一个,剩余 \(2N-K\) 个。
对这 \(2N-K\) 个数进行两两组合(可能剩余 \(1\) 个),使得每对数之差的绝对值之和最小。输出这个最小和。

\(1\le K\le N\le 2\times 10^5\)
\(1\le A_1<A_2<\dots<A_K\le N\)

分析

首先,可以证明我们一定会将 \(N-K\) 个成双的数字进行自我组合。

简要证明
采用 反证法。假设有两个 \(a\),我们将它们分别与 \(b,c\) 组合。显然:

\[|a-b|+|a-c| \ge |a-a|+|b-c| \]

于是,将 \(a,a\) 组合、\(b,c\) 组合的方案一定不比原方案差。因此直接组合 \(a,a\),一定能得到最优解。

由于 \(|a-a|=0\),所以这部分可以直接忽略,将单个的数字,即 \(A_1,\dots,A_K\) 进行组合即可。

分两种情况讨论。

  1. \(K\) 为偶数:此时组合没有剩余。不难发现,相邻两两组合即为最优解,所以答案为:

    \[\mathrm{ans}=\sum_{i=1}^{k/2} A_{2i}-A_{2i-1} \]

    直接计算即可。注意 \(A\) 已排序,故无需取绝对值。

  2. \(K\) 为奇数:此时组合剩余一个。枚举此剩余的数,从 \(A\) 中删去就转换成了偶数的情况。但是暴力计算的时间复杂度为 \(\mathcal O(K^2)\),维护前缀后缀和即可优化到 \(\mathcal O(K)\)

综上,我们在 \(\mathcal O(K)\) 的时间内解决了此问题。

代码

#include <cstdio>
#define maxn 200005
using namespace std;inline void setmin(int& x, int y)
{if(y < x) x = y;
}int a[maxn], pre[maxn], suf[maxn];int main()
{int n, k;scanf("%d%d", &n, &k);for(int i=1; i<=k; i++)scanf("%d", a + i);if(!(k & 1)){int ans = 0;for(int i=1; i<=k; i+=2)ans += a[i + 1] - a[i];printf("%d\n", ans);return 0;}for(int i=1; i<k; i+=2)pre[i] = pre[i + 1] = pre[i - 1] + a[i + 1] - a[i];for(int i=k-1; i>0; i-=2)suf[i] = suf[i + 1] = suf[i + 2] + a[i + 1] - a[i];int ans = 1e9;for(int i=1; i<=k; i++){int cur = i & 1? pre[i - 1] + suf[i + 1]: a[i + 1] - a[i - 1] + pre[i - 2] + suf[i + 2];setmin(ans, cur);}printf("%d\n", ans);return 0;
}

D - Reindeer and Sleigh

题目大意

\(N\) 个雪橇,编号为 \(1,2,\dots,N\)。拉动第 \(i\) 个雪橇需要 \(R_i\) 只驯鹿。

给定 \(Q\) 次询问,每次给定正整数 \(X\)

  • \(X\) 只驯鹿最多能拉动多少个雪橇?

注意:雪橇可以任选,每只驯鹿最多只能拉一个雪橇。

\(1\le N,Q\le 2\times 10^5\)
\(1\le R_i\le 10^9\)
\(1\le X\le 2\times 10^{14}\)

分析

首先,为了拉到最多的雪橇,我们考虑贪心的策略:从 \(R_i\) 最小的雪橇开始拉,从小到大直到驯鹿不够用为止。

因此我们先对 \(R_i\) 进行排序,很明显这不影响结果。此时令前缀和 \(S_i=\sum_{j=1}^i R_j\),则当 \(S_i \le X\) 时,可以拉动前 \(i\) 个雪橇。故只需找到最大的 \(i\) 使得 \(S_i\le X\) 即为所求。此时注意到前缀和已经有序,所以直接在 \(S\) 上使用二分查找即可。

总时间复杂度为 \(\mathcal O(N\log N)\)

代码

#include <cstdio>
#include <algorithm>
#define maxn 200005
using namespace std;using LL = long long;
LL s[maxn];int main()
{int n, q;scanf("%d%d", &n, &q);for(int i=0; i<n; i++)scanf("%lld", s + i);sort(s, s + n);for(int i=1; i<n; i++)s[i] += s[i - 1];while(q--){LL x;scanf("%lld", &x);printf("%d\n", int(upper_bound(s, s + n, x) - s));}return 0;
}

E - Christmas Color Grid 1

题目大意

有一个 \(H\times W\) 的网格,其中.代表红色#代表绿色

随机选一个红色方块,将其涂成绿色。将网格抽象成一张简单无向图,边连接相邻(上下左右)的绿色节点。
图中连通分量个数的期望值是多少?对 \(998244353\) 取模。

\(1\le H,W\le 1000\)

分析

暴力算法的时间复杂度是 \(\mathcal O(H^2W^2)\),显然不满足要求。

考虑将一个红色方块涂成绿色绿色连通分量数的贡献。令它周围属于不同连通分量的绿色方块个数为 \(n\),则此次操作会将答案减去 \(n-1\)。这样,我们先预处理出连通分量,就可以 \(\mathcal O(1)\) 的计算答案。

此问题可以用 DFS、BFS 或并查集解决。示例代码使用并查集,时间复杂度约为 \(\mathcal O(HW)\)(忽略小函数)。

代码

#include <cstdio>
#include <unordered_map>
#include <set>
#include <atcoder/modint>
#define maxn 1005
using namespace std;using modint = atcoder::modint998244353;int n, m, fa[maxn * maxn];
char s[maxn][maxn];int find(int x) { return fa[x] == x? fa[x]: fa[x] = find(fa[x]); }
inline int calc(int x, int y) { return x * m + y; }
inline int fc(int x, int y) { return find(calc(x, y)); }
inline void merge(int x, int y) { fa[find(x)] = find(y); }int main()
{scanf("%d%d", &n, &m);for(int i=0; i<n; i++)scanf("%s", s[i]);int k = n * m;for(int i=0; i<k; i++)fa[i] = i;for(int i=0; i<n; i++)for(int j=0; j<m; j++){if(s[i][j] != '#') continue;if(i && s[i - 1][j] == '#') merge(calc(i, j), calc(i - 1, j));if(j && s[i][j - 1] == '#') merge(calc(i, j), calc(i, j - 1));}int cnt = 0, tot = 0;for(int i=0; i<n; i++)for(int j=0; j<m; j++)if(s[i][j] == '#' && fc(i, j) == calc(i, j))cnt ++;modint ans = 0;for(int i=0; i<n; i++)for(int j=0; j<m; j++)if(s[i][j] == '.'){set<int> S;if(i && s[i - 1][j] == '#') S.insert(fc(i - 1, j));if(s[i + 1][j] == '#') S.insert(fc(i + 1, j));if(j && s[i][j - 1] == '#') S.insert(fc(i, j - 1));if(s[i][j + 1] == '#') S.insert(fc(i, j + 1));int cur = cnt - (int)S.size() + 1;ans += cur, tot ++;}printf("%d\n", (ans / tot).val());return 0;
}

F - Christmas Present 2

题目大意

圣诞老人 Santa 要在平面直角坐标系中给孩子们送礼物啦!

他的家在 \((S_X,S_Y)\) 处。他要按照数字顺序给 \(N\) 个孩子送出礼物。第 \(i\) 个孩子的家在 \((X_i,Y_i)\) 处。

Santa 手上最多只能一次性拿 \(K\) 个礼物。他想用最短的路程送完所有礼物,再回到自己家,求最短的总路程是多少?

\(1\le K\le N\le 2\times 10^5\)
\(-10^9\le S_X,S_Y,X_i,Y_i \le 10^9\)
\((S_X,S_Y)\ne (X_i,Y_i)\)
\((X_i,Y_i)\ne (X_j,Y_j)\ (i\ne j)\)

分析

这里介绍我自己的独具特(chōu)色(xiàng)的解法,常规解法请参考官方题解。

考虑 dp,令 \(f_{i,j}\) 表示走到第 \(i\) 个房子并送完前 \(i\) 个礼物时,手上剩余 \(j\) 个礼品的最短路程。很显然,我们每次回家都一定拿满 \(K\) 个礼品,则 \(j=k-1\) 一定是拿了礼品之后送出一个。故:

\[f_{i,j}=\begin{cases} d(i-1,i)+f_{i-1,j+1} & (j < k-1)\\ \min f_{i-1}+d(i-1,0)+d(0,i) & (j=k-1) \end{cases} \]

其中 \(d(a,b)\) 表示房子 \(a\)\(b\) 的路程。特别规定 \(0\) 号房子为 \((S_X,S_Y)\),即圣诞老人的住处。这样,答案即为 \(\min f_n+d(n,0)\)

直接计算的复杂度为 \(\mathcal O(NK)\),时间和空间上都不能接受。

然而,仔细观察递推式可以发现,\(f_i\) 这一行实际上就是由前一行 \(f_{i-1}\) 删去第一个元素,再整体加 \(d(i-1,i)\),并在最后添上 \(f_{i,k-1}\) 得到的。

因此,我们可以用一个deque(双端队列)动态维护状态。对于整体加的操作,用一个变量维护整体的变化值即可。这样空间的问题就得到了解决。再进一步考虑,用一个multiset(可重集合,基于红黑树)或者二叉堆维护队列内元素,求 \(\min\) 的操作时间就减小到了 \(\mathcal O(\log K)\),可以接受。

于是,我们就成功地在 \(\mathcal O(N\log K)\) 的时间和 \(\mathcal O(N+K)\) 的空间内解决了此问题。另外,我们还可以把deque同时充当单调队列,这样时间也优化到了 \(\mathcal O(N+K)\)。两种实现的示例代码都会给出。

代码

实现 1:deque + multiset

#include <cstdio>
#include <deque>
#include <set>
#define maxn 200005
using namespace std;using ld = long double;
const ld INF = 2e18l;int x[maxn], y[maxn];inline ld dis(int i, int j)
{return __builtin_hypotl(x[i] - x[j], y[i] - y[j]);
}int main()
{int n, k;scanf("%d%d", &n, &k);for(int i=0; i<=n; i++)scanf("%d%d", x + i, y + i);deque<ld> f;multiset<ld> s;k --;for(int i=0; i<k; i++)f.push_back(INF), s.insert(INF);f.push_back(dis(0, 1)), s.insert(dis(0, 1));ld dt = 0;for(int i=2; i<=n; i++){ld lt = *s.begin() + dis(i - 1, 0) + dis(0, i) + dt;s.erase(s.find(f.front())), f.pop_front();dt += dis(i - 1, i), lt -= dt;f.push_back(lt), s.insert(lt);}printf("%.15Lf\n", dt + *s.begin() + dis(n, 0));return 0;
}

实现 2:单调队列

#include <cstdio>
#include <deque>
#define maxn 200005
using namespace std;int x[maxn], y[maxn];inline double dis(int i, int j)
{return __builtin_hypotl(x[i] - x[j], y[i] - y[j]);
}int main()
{int n, k;scanf("%d%d", &n, &k);for(int i=0; i<=n; i++)scanf("%d%d", x + i, y + i);deque<pair<double, int>> f;f.emplace_back(dis(0, 1), 1);double dt = 0;for(int i=2; i<=n; i++){double lt = f.front().first + dis(i - 1, 0) + dis(0, i) + dt;if(f.front().second == i - k) f.pop_front();dt += dis(i - 1, i), lt -= dt;while(!f.empty() && f.back().first >= lt) f.pop_back();f.emplace_back(lt, i);}printf("%.15lf\n", dt + f.front().first + dis(n, 0));return 0;
}

G - Christmas Color Grid 2

题目大意

有一个 \(H\times W\) 的网格,其中.代表红色#代表绿色

随机选一个绿色方块,将其涂成红色。将网格抽象成一张简单无向图,边连接相邻(上下左右)的绿色节点。
图中连通分量个数的期望值是多少?对 \(998244353\) 取模。

\(1\le H,W\le 1000\)

E 与 G 的区别

  • E:将红色涂成绿色。求绿色连通块个数。
  • G:将绿色涂成红色。求绿色连通块个数。

分析

注意到本题中红色方块没有任何实质意义,于是先将绿色方块建成一张图。此时题目变为:

  • 从简单无向图中随机选取一个结点,将此结点和与其相连的边全部删除。求连通分量个数的期望值,对 \(998244353\) 取模。

根据“删去无向图中一个点导致连通分量个数改变”,很容易联想到割点。对求割点的 Tarjan 算法稍加改编,就可以计算删去一个点能把图分割成的连通块个数。详见代码。

代码

#include <cstdio>
#include <vector>
#include <atcoder/modint>
#define maxn 1000005
using namespace std;using modint = atcoder::modint998244353;inline void setmin(int& x, int y)
{if(y < x) x = y;
}vector<int> G[maxn];inline void add(int x, int y)
{G[x].push_back(y);G[y].push_back(x);
}int root, low[maxn], cnt, dfn[maxn], ncut[maxn];void tarjan(int v)
{low[v] = dfn[v] = ++cnt;ncut[v] = v != root;for(int u: G[v])if(!dfn[u]){tarjan(u);if(low[u] >= dfn[v])ncut[v] ++;setmin(low[v], low[u]);}else setmin(low[v], dfn[u]);
}char s[1005][1005];
int id[1005][1005];int main()
{int n, m;scanf("%d%d", &n, &m);for(int i=1; i<=n; i++)scanf("%s", s[i] + 1);int num = 0;for(int i=1; i<=n; i++)for(int j=1; j<=m; j++)if(s[i][j] == '#'){id[i][j] = ++num;if(s[i - 1][j] == '#') add(num, id[i - 1][j]);if(s[i][j - 1] == '#') add(num, id[i][j - 1]);}int cc = -1;for(int i=1; i<=num; i++)if(!dfn[i])tarjan(root = i), cc ++;modint ans = 0;for(int i=1; i<=num; i++)ans += cc + ncut[i];printf("%d\n", (ans / num).val());return 0;
}

后记

首先预祝大家圣诞节快乐 🎉!这场比赛从标题到题目设定,无不与圣诞节有关,AtCoder 官方算是精心准备了这场圣诞庆祝赛 💝。

遗憾的是我在比赛中先做了 A~E 和 G,F 题比赛结束后 51s 提交,AC。挺可惜的,难得 G 能做出来一次,差点 AK,结果差的就是不到一分钟 😂。希望下次能比得更好,也希望大家能再接再厉。加油!😚

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

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

相关文章

Mathematica 入门

前言 Wolfram Mathematica(简称 MMA),是由 Wolfram Research 开发的科学计算软件。本文我们将介绍 Mathematica 的界面、语法和基本应用。类似的软件还有 MATLAB 和 Maple 等。 MMA 官网:https://www.wolfram.com/mathematica/ MMA 的安装及激活:Mathematica安装激活极简教…

【算法笔记】位运算详解

0. 前言 突然想到位运算是个好东西,就来水一波文章了…… 注意:我把能想到的有关位运算的所有内容都放进来了,所以篇幅较长,请谅解!若有写的不清楚或者不够详细的地方欢迎在评论区补充,谢谢支持! 本文中参考代码均使用C++编写。 废话不多说,下面步入正题。1. 基本运算 …

【算法笔记】【专题】RMQ 问题:ST表/树状数组/线段树

0. 前言 好久没更算法笔记专栏了,正好学了新算法来更新…… 这也是本专栏的第一个专题问题,涉及到三种数据结构,如果写得有问题请各位大佬多多指教,谢谢! 1. 关于 RMQ 问题 RMQ 的全称是 Range Minimum/Maximum Query,即区间最大/最小值问题。 本文中,我们的算法以求最大…

【算法笔记】树形DP算法总结详解

0. 定义 树形DP,又称树状DP,即在树上进行的DP,是DP(动态规划)算法中较为复杂的一种。 1. 基础 令\(f[u]=~\)与树上顶点\(u\)有关的某些数据,并按照拓扑序(从叶子节点向上到根节点的顺序)进行\(\text{DP}\),确保在更新一个顶点时其子节点的dp值已经被更新好,以更新当前…

Seay安装和初步使用

作者网站现在已经无法访问:http://www.cnseay.com/2951/, 可以使用这个GitHub - f1tz/cnseay: Seay源代码审计系统 下载完安装包之后,解压到自己想要的电脑路径即可,无需进行任何额外的配置 利用工具对sql注入进行分析 进入软件之后,点击新建项目,选择需要分析的文件(这里…

纸浆

日线: 小时线: 15分钟趋势: 关注5750一线支撑

代码整洁之道--读书笔记(5)

代码整洁之道简介: 本书是编程大师“Bob 大叔”40余年编程生涯的心得体会的总结,讲解要成为真正专业的程序员需要具备什么样的态度,需要遵循什么样的原则,需要采取什么样的行动。作者以自己以及身边的同事走过的弯路、犯过的错误为例,意在为后来者引路,助其职业生涯迈上更…

02网络参考模型

02网络参考模型02网络参考模型常见网络模型因为 OSI协议栈比较复杂 ,且TCP和IP两大协议在业界被广泛使用,所以 TCP/IP参考模型 成为了互联网的主流参考模型。OIS网络模型层级作用7.应用层 应用层 对应用程序提供接口。6.表示层进行数据格式的转换,以确保一个系统生成的应用层…

《痞子衡嵌入式半月刊》 第 107 期

痞子衡嵌入式半月刊: 第 107 期这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿。 上期回顾 :《…

鼠标悬停显示的轮播图

今日整理,发现这种轮播图是最难实现的一种, 1.再循环中难以控制单一品类商品显示 解决办法: 在外面的主类里面添加&:hover触发标签属性的更改,这样可以单一作用 2.在循环中触发事件,所有的同一事件都会触发 解决办法:先建立模版控制排版,再从单一内容开始微调 <script s…

如何把网页的公式优雅地拷贝到word中:数学公式识别神器—Mathpix Snip

这个编辑器其实在把chatgpt的公式粘贴到word中时就已经使用了,用的是网页版。 现在下载了软件(但是好像一个月试用期过后得收费?但是就目前来说,体验感真的超级好) 把公式复制粘贴转成mathtype公式 可以截取电脑屏幕上的图像,如果图像上面有公式的话,就会识别,之后可以…

Redis 入门 - 图形化管理工具如何选择,最全分类

Redis图形化管理工具可分为四类:命令行工具、桌面客户端工具、网页工具、插件工具。看看哪一款适合你呢?工欲善其事必先利其器,上一章Redis服务环境已经搭建完成,现在就需要一个趁手的工具,有个好工具可以做到事半功倍。 Redis图形化管理工具五花八门,可供选择的很多,大…