当前位置: 首页 > news >正文

2022 ICPC Jinan DG and 2022 ICPC Nanjing

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.hskmm.com/?act=detail&tid=36203

相关文章:

  • 图像分割 sam1 - MKT
  • SDL-1
  • CF1206B Make Product Equal One
  • 软件工程第三次作业----结对项目
  • 关于莫比乌斯函数的应用1
  • 用deepseek写的一个求原根的程序
  • 操作备忘:在AE中让视频中间部分变慢
  • 记一次精简系统Windows11英文版离线安装中文语言包的过程
  • 阿里巴巴数据库开发手册
  • AI元人文:赋能公共治理、司法与监管的价值权衡新范式
  • 基础的sql练习,全都理解你就是高手了!
  • nginx快速实现平滑版本升级
  • Luogu P11159 【MX-X6-T5】 再生 题解 [ 蓝 ] [ 前缀和 ] [ 组合计数 ]
  • 王浩宇 102500416
  • 程序员修炼之路:从小工到专家 读书笔记 2
  • 程序员修炼之路:从小工到专家 读书笔记 3
  • 中级问题
  • 2025.10.21
  • 解答在同步以太坊事件数据时,如何保证后端服务在 API/RPC 不稳定情况下的可用性
  • 程序员修炼之道:从小工到专家 读书笔记 1
  • 好想好想你
  • 10.21日学习笔记
  • 数据库概述
  • 第1天(简单题 基础语法 数据类型、条件判断 、循环 循环嵌套、位运算, ASCII 码)
  • 24信计2班 17曾向嵩 pytorch读书报告
  • 关于第一次作业的时长统计
  • Go 语言问题解释
  • Keil_v5的用法
  • day 8
  • OI 笑传 #21