2022 ICPC Jinan DG and 2022 ICPC Nanjing

news/2025/10/22 1:37:53/文章来源:https://www.cnblogs.com/Young-Cloud/p/19156754

2022 ICPC Jinan DG and 2022 ICPC Nanjing

2022 Jinan

D

需要考虑的地方是 ? 类型的提交,对于每种这样的提交,我们可以算出它可产生的最小罚时和最大罚时。于是我们单独考虑这样的提交,二进制枚举那些提交过了,判断一下可不可能合法。枚举出那些 ? 罚时是能够通过的后,就开始构造了,思路就是枚举每个 ? 类型的提交时都尽可能让它的罚时大。具体看代码就好了:

constexpr int N = 1000;struct Submit {int id; // 题号char type; // 类型int a, b;
};
struct Froz {int id;int a, b; // 封榜后的提交数和总提交数int mn, mx;Froz(int _id, int _a, int _b) : id(_id), a(_a), b(_b) {mn = (b - a) * 20 + 240; // 封榜后立刻过过mx = (b - 1) * 20 + 299; // 直到最后一刻才过}
};int M;bool solve() {// 题数和罚时int n = 0, p = 0;std::cin >> n >> p;int cntn = 0, cntp = 0;std::vector<Froz> pending;std::vector<Submit> all(M);for (int i = 0; i < M; ++i) {all[i].id = i;char t;std::cin >> t;all[i].type = t;if (t == '-') {std::cin >> all[i].b;}else if (t == '?'){std::cin >> all[i].a >> all[i].b;pending.push_back(Froz(i, all[i].a, all[i].b));}else if (t == '+'){scanf("%d/%d", &all[i].a, &all[i].b);cntn += 1;cntp += all[i].b + (all[i].a - 1) * 20;}}int m = n - cntn; // 封榜后的过题数if (m < 0) {std::cout << "No\n";return true;}int t = pending.size();if (m > t) {std::cout << "No\n";return true;}int u;int mn, mx;for (u = 0; u < (1 << t); ++u) {if (std::__popcount(u) != m) {continue;}mn = 0, mx = 0;for (int i = 0; i < t; ++i) {if (u >> i & 1) {mn += pending[i].mn;mx += pending[i].mx;}}if (cntp + mn <= p && p <= cntp + mx) {break;}}if (u == (1 << t)) {std::cout << "No\n";return true;}for (int i = 0; i < t; ++i) {if (!(u >> i & 1)) {continue;}int id = pending[i].id;all[id].type = '+';all[id].a = (pending[i].b - pending[i].a + 1);all[id].b = 240;mn -= pending[i].mn;cntp += pending[i].mn;while (all[id].a < pending[i].b) {if (cntp + 20 + mn <= p) {cntp += 20;all[id].a += 1;}else {break;}}while (all[id].b < 299) {if (cntp + 1 + mn <= p) {cntp += 1;all[id].b += 1;}else {break;}}}std::cout << "Yes\n";for (int i = 0; i < M; ++i) {if (all[i].type == '+') {std::cout << "+ " << all[i].a << '/' << all[i].b << '\n';}else if (all[i].type == '.') {std::cout << ".\n";}else {std::cout << "- " << all[i].b << '\n';}}return true;
}int main () {// IOS;int T = 1;std::cin >> T;std::cin >> M;while (T--) {solve();// std::cout << (solve() ? "YES" : "NO") << '\n';}return 0;
}

G

分析一下这个排序算法,发现瓶颈在于 PARTITION 函数中的双指针找数,就是在特定区间找一个小于等于或大于等于一个阈值的数,直接线段树维护区间最值然后线段树上二分就好了:

constexpr int N = 5e5, Inf = 1e9;struct info {int mn, mx;info() : mn(Inf), mx(-Inf) {};info(int val) : mn(val), mx(val) {};info (const info l, const info r) {mn = std::min(l.mn, r.mn);mx = std::max(l.mx, r.mx);}info operator+ (const info &r) {return info(*this, r);}
} tr[N << 2];int n;
int a[N + 5];
int ans;void push_up(int cur) {tr[cur] = tr[cur << 1] + tr[cur << 1 | 1];
}
void build(int cur, int l, int r) {if (l == r) {tr[cur] = info(a[l]);return;}int m = l + r >> 1;build(cur << 1, l, m);build(cur << 1 | 1, m + 1, r);push_up(cur);
}
void upd(int cur, int l, int r, int pos) {if (l == r) {tr[cur] = info(a[pos]);return;}int m = l + r >> 1;if (pos <= m) {upd(cur << 1, l, m, pos);}else {upd(cur << 1 | 1, m + 1, r, pos);}push_up(cur);
}
// 在 [sl, sr] 中找最左边的大于等于 val 的位置
int findLnoLS(int cur, int l, int r, int sl, int sr, int val) {if (r < sl || l > sr) {return Inf;}if (sl <= l && r <= sr) {if (tr[cur].mx < val) {return Inf;}while (l < r) {int m = l + r >> 1;cur <<= 1;if (tr[cur].mx >= val) {r = m;}else {l = m + 1;cur |= 1;}}return l;}int m = l + r >> 1;return std::min(findLnoLS(cur << 1, l, m, sl, sr, val), findLnoLS(cur << 1 | 1, m + 1, r, sl, sr, val));
}
// 在 [sl, sr] 中找最右边的小于等于 val 的位置
int findRnoGR(int cur, int l, int r, int sl, int sr, int val) {if (r < sl || l > sr) {return -Inf;}if (sl <= l && r <= sr) {if (tr[cur].mn > val) {return -Inf;}while (l < r) {int m = l + r >> 1;cur <<= 1;if (tr[cur | 1].mn <= val) {l = m + 1;cur |= 1;}else {r = m;}}return l;}int m = l + r >> 1;return std::max(findRnoGR(cur << 1, l, m, sl, sr, val), findRnoGR(cur << 1 | 1, m + 1, r, sl, sr, val));
}
void swap(int x, int y) {std::swap(a[x], a[y]);upd(1, 1, n, x);upd(1, 1, n, y);return;
}int find(int l, int r) {int val = a[l + r >> 1];int i = l - 1, j = r + 1;while (true) {i = findLnoLS(1, 1, n, i + 1, n, val);j = findRnoGR(1, 1, n, 1, j - 1, val);if (i >= j) {return j;}else {swap(i, j);ans += 1;}}
}void qsort(int l, int r) {if (l >= 0 && r >= 0 && l < r) {int m = find(l, r);qsort(l, m);qsort(m + 1, r);}
}void init(int n) {ans = 0;build(1, 1, n);
}bool solve() {;std::cin >> n;for (int i = 1; i <= n; ++i) {std::cin >> a[i];}init(n);qsort(1, n);std::cout << ans << '\n';return true;
}

2022 Nanjing

A

在没有洞的情况下,模拟这个过程,最后可以直到那块区域中的袋鼠留在了最后,对于最后可能留下来的袋鼠,我们跟踪最坐上角的一只,看他会经过那些格子,用 set 记录一下。对于其他袋鼠,经过的格子相当于是第一只袋鼠经过的格子向下向右平移后得来的,用二维前缀和维护出每个格子被多少只不同的袋鼠经过过,然后合法的格子就是被 \(lx\times ly - k\) 只袋鼠经过的格子的数量,其中 \(lx\) \(ly\) 是最后可能留下来的袋鼠所占区域的长宽:

int dx['Z' + 1], dy['Z' + 1];int pre[N + 5][N + 5];void init(int n, int m) {for (int i = 1; i <= n; ++i) {for (int j = 1; j <= m; ++j) {pre[i][j] = 0;}}
}void add(int mnx, int mny, int mxx, int mxy) {pre[mnx][mny] += 1;pre[mxx + 1][mxy + 1] += 1;pre[mnx][mxy + 1] -= 1;pre[mxx + 1][mny] -= 1;
}int solve() {int n = 0, m = 0, k = 0;std::string s;std::cin >> n >> m >> k >> s;init(n, m);int mnx = 1, mny = 1;int mxx = n, mxy = m;int Dx = 0, Dy = 0;for (auto &c : s) {mnx = std::max(1, mnx + dx[c]);mny = std::max(1, mny + dy[c]);mxx = std::min(n, mxx + dx[c]);mxy = std::min(m, mxy + dy[c]);Dx += dx[c];Dy += dy[c];if (mxx <= 0 || mxy <= 0 || mnx > n || mny > m) {if (k == 0) {return n * m;}else {return 0;}}}mnx -= Dx, mny -= Dy, mxx -= Dx, mxy -= Dy;int lx = mxx - mnx, ly = mxy - mny;std::set<std::pair<int, int>> S;S.insert({ mnx, mny });for (auto &c : s) {mnx += dx[c];mny += dy[c];S.insert({ mnx, mny });}for (auto &[x, y] : S) {add(x, y, x + lx, y + ly);}for (int j = 1; j <= m; ++j) {for (int i = 1; i <= n; ++i) {pre[i][j] += pre[i - 1][j];}}for (int i = 1; i <= n; ++i) {for(int j = 1; j <= m; ++j) {pre[i][j] += pre[i][j - 1];// std::cout << pre[i][j] << ' ';}// std::cout << '\n';}k = (lx + 1) * (ly + 1) - k;int ans = 0;for (int i = 1; i <= n; ++i) {for (int j = 1; j <= m; ++j) {if (pre[i][j] == k) {ans += 1;}}}return ans;
}int main () {IOS;int T = 1;std::cin >> T;dx['U'] = -1, dy['U'] = 0;dx['D'] = 1, dy['D'] = 0;dx['L'] = 0, dy['L'] = -1;dx['R'] = 0, dy['R'] = 1;while (T--) {std::cout << solve() << '\n';}return 0;
}

D

直接二分答案\(lim\),问题在于如何检查

想知道一个序列的第 \(k\) 大的数能不能大于等于 \(lim\),只需要知道这个序列中大于等于 \(lim\) 的数有没有等于或超过 \(k\) 个。与其考虑选择一个区间会让多少个数不小于 \(lim\),不如直接判断对于一个数,有哪些区间能够使他不少于 \(lim\),这样对所有数扫一遍后,我们就知道了每个区间能使多少个数不少于 \(lim\),看有没有一个大于等于 \(k\) 就好了。具体的,对于原本就不小于的 \(lim\) 的数,我们认为所有区间都能让它不小于 \(lim\);对于最大(\(a_i + c + (m - 1)d\))还小于 \(lim\) 的数,我们认为所有区间都不能让它不小于 \(lim\);其他的,我们首先判断判断是否满足 \(lim \leq a_i + c\),此时只要包含 \(i\) 的区间都满足条件,否则就要算一下满足条件的区间有哪些:

constexpr int N = 2e5;int n, k, m;
i64 c, d;
i64 a[N + 5];
int pre[N + 5];bool chk(i64 lim) {int L = 1, R = n - m + 1;for (int i = L; i <= R + 1; ++i) {pre[i] = 0;}for (int i = 1; i <= n; ++i) {if (a[i] >= lim) {pre[1] += 1;continue;}if (a[i] + c + (m - 1) * d < lim)  {continue;}int l = std::max(L, i - m + 1);int r = R;if (a[i] + c >= lim) {r = std::min(r, i);}else {r = std::min(r, i - (int)((lim - c - a[i] - 1) / d + 1));}if (l <= r) {pre[l] += 1;pre[r + 1] -= 1;}}for (int i = L; i <= R + 1; ++i) {pre[i] += pre[i - 1];if (pre[i] >= k) {return true;}}return false;
}void solve() {std::cin >> n >> k >> m >> c >> d;for (int i = 1; i <= n; ++i) {std::cin >> a[i];}i64 l = 0, r = Inf;while (l <= r) {i64 mid = (l + r >> 1);if (chk(mid)) {l = mid + 1;}else {r = mid - 1;}}std::cout << r << '\n';return;
}

M

比较 ez 的计算几何,看代码就好了:

using node = std::array<i64, 2>;node operator- (const node &u, const node &v) {return { u[0] - v[0], u[1] - v[1] };
}i64 operator^ (const node &u, const node &v) {return u[0] * v[1] - u[1] * v[0];
}int solve() {int n = 0;std::cin >> n;std::vector p(n + 2, node{});for (int i = 1; i <= n; ++i) {std::cin >> p[i][0] >> p[i][1];}p[0] = p[n];p[n + 1] = p[1];std::vector<int> Y;for (int i = n; i >= 1; --i) {if (p[i][1] != p[1][1]) {Y.push_back(p[i][1]);break;}}int ans = 0;for (int i = 1; i <= n; ++i) {if (Y.back() != p[i][1]) {Y.push_back(p[i][1]);}i64 cross = ((p[i - 1] - p[i]) ^ (p[i + 1] - p[i]));if (cross >= 0) {continue;}int a = Y[Y.size() - 2];int b = Y.back();int c = p[i + 1][1];if (a > b && c > b) {ans += 1;}}return ans;
}

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

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

相关文章

你的开发服务器在说谎-热重载与热重启的关键区别

GitHub 主页 你的开发服务器在说谎:热重载与热重启的关键区别 🔥🔄🚀 作为开发者,我们都迷恋那种“心流”状态。当你全神贯注,代码从指尖流淌而出,每一次保存,终端里的服务就自动重启,浏览器一刷新,新的变…

SDL-1

1.https://www.cppgamedev.top/courses/sdl-space-shooter/parts/sdl-fundamentals 练习3:添加音效播放功能(使用Mix_LoadWAV和Mix_PlayChannel函数) 1.SDL使用的音频数据结构 chunk完全预先加载进内存的文件 music …

CF1206B Make Product Equal One

CF1206B Make Product Equal One题目描述 给你一个有 n 个数的数组。你可以用 x(x为任意正整数) 的代价将数组中的任意一个数增加或减少 x ,你可以重复多次此操作。现在需要你用若干次操作使得 a_1a_2...a_n = 1 (数组…

关于莫比乌斯函数的应用1

include include include include include using namespace std; // 快速幂算法:计算 (a^b) % mod long long fast_power(long long a, long long b, long long mod) { long long result = 1; a = a % mod; whil…

关于莫比乌斯函数的应用

include include include int main() { constexpr int M = 20101009; int n, m; std::cin >> n >> m; if (n > m) std::swap(n, m); std::vector f(n + 1), vis(n + 1), prime; prime.reserve(n); f…

软件工程第三次作业----结对项目

一、作业信息github网址 https://github.com/easytime2000/MathApp这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/?page=3这个作业要求在哪里 https://edu.cnblogs.com/c…

用deepseek写的一个求原根的程序

include include include include include using namespace std; // 快速幂算法:计算 (a^b) % mod long long fast_power(long long a, long long b, long long mod) { long long result = 1; a = a % mod; whil…

操作备忘:在AE中让视频中间部分变慢

目标效果:一个15秒的视频, 1-5秒的部分1倍速播放 5-10秒的部分以0.5倍速播放 10-15秒的部分1倍速播放。 操作如下: 1. 对素材或预合成启用时间重映射 2. 在5秒、10秒处打关键帧(开始变慢与结束变慢的位置): 3.…

记一次精简系统Windows11英文版离线安装中文语言包的过程

最近折腾上了超级精简的Win11系统,就是没有那些乱七八糟的服务的,也没有乱七八糟自带软件的系统,然后看了一圈,发现一个叫Nano11的精简系统非常不错,系统的项目以及下载地址:https://github.com/ntdevlabs/nano11…

阿里巴巴数据库开发手册

下载地址https://8ma.co/res/RMFS3J81.zstitle { width: 280px; text-align: center; font-size: 26px } .zsimgweixin { width: 280px } .zsimgali { width: 280px; padding: 0px 0px 50px 0px } .zsleft { float: le…

AI元人文:赋能公共治理、司法与监管的价值权衡新范式

AI元人文:赋能公共治理、司法与监管的价值权衡新范式 在当今复杂多元的社会环境中,公共治理、司法审判与行业监管领域面临着日益复杂的价值权衡挑战。经济发展与环境保护、个人权益与公共利益、创新活力与风险防控等…

数据结构之顺序队列

数据结构之顺序队列数据结构之队列 什么是队列队列是和栈一样操作受限的线性表,栈是只允许在线性表的一端进行入栈和出栈操作,而队列是会允许在线性表的一端进行入队,在另外一端进行出队操作队列的基本操作 bool in…

nginx快速实现平滑版本升级

1、解压并编译新版的nginx # 目前版本为1.18,解压一个1.21版本的nginx包 wget https://nginx.org/download/nginx-1.21.0.tar.gz tar -zxf nginx-1.21.0.tar.gz cd nginx-1.21.0/ [root@ubt-server nginx-1.21.0]# ls…

基础的sql练习,全都理解你就是高手了!

以下sql我都是亲测:大多数用法都会在面试当中被问到,切记一步一个脚印的去实现,结果不重要,重要的是你的实现过程的想法,第一步做什么然后第二步做什么等具体的详细过程!(学东西不能贪多,慢慢来) 先从单表查询…

Luogu P11159 【MX-X6-T5】 再生 题解 [ 蓝 ] [ 前缀和 ] [ 组合计数 ]

再生 笑点解析:一开始乘法原理推错式子胡了个依赖链长种类数 \(\le \sqrt n\) 的做法上去。 有了 \(top\) 数组,显然可以求出每个点所处的长链。对于长链上的点,如果链长为 \(x\),那么这条链有 \((x - 1)!\) 种可能…

王浩宇 102500416

这个作业属于:https://edu.cnblogs.com/campus/fzu/gjyycx 这个作业的要求:https://edu.cnblogs.com/campus/fzu/gjyycx/homework/13570 学号:102500416 姓名:王浩宇 书本作业 第一题第二题第三题第四题第五题第六…

102500416 王浩宇

这个作业属于:https://edu.cnblogs.com/campus/fzu/gjyycx 这个作业的要求:https://edu.cnblogs.com/campus/fzu/gjyycx/homework/13570 学号:102500416 姓名:王浩宇 书本作业 第一题第二题第三题第四题第五题第六…

程序员修炼之路:从小工到专家 读书笔记 2

《程序员修炼之道:从小工到专家》读书笔记(补充篇) 重读《程序员修炼之道》,除了此前感悟的核心原则,书中 “破窗理论”“原型验证”“责任承诺” 等理念,更让我看清从 “完成代码” 到 “掌控开发” 的进阶细节…

程序员修炼之路:从小工到专家 读书笔记 3

《程序员修炼之道:从小工到专家》读书笔记(进阶篇) 三读《程序员修炼之道》,书中 “知识负债”“自动化思想”“沟通协作” 三大被忽略的理念,终于让我触摸到 “专家” 的核心特质 —— 不仅是技术能力的精进,更…

程序员修炼之道:从小工到专家 读书笔记 1

《程序员修炼之道:从小工到专家》读书笔记 翻开《程序员修炼之道:从小工到专家》,没有复杂的代码堆砌,却满是对程序员职业成长的深刻洞察。这本书更像一位资深前辈的经验分享,指引着开发者跳出 “代码搬运工” 的…