The 2023 ICPC Asia Hangzhou Regional Contest
写在前面
赛时题目按照过题顺序排序,赛后补题按照个人向难度排序。
虽然补题大概要拖到期末之后了。
这学期确实是超负荷了,现在脑子里一团糟,赛时的记忆已经不太清楚了。
省流版:搏一搏单车变摩托,但是怂了。
赛时
M
开局我正开,有中文题面好评。
过了十几分钟发现 M 过了挺多,于是 Nebulyu 拉我来讨论一波,先出了一个假做法挂了一发。
期间 dztle 也加入了讨论,发现可以从给的数列两端依次删数来调整达到最值,优先队列实现即可,三十分钟的时候才过,属于是非常恶劣的开局。
J
在 Nebulyu 写 M 的期间我和 dztle 一直在推 D 但是没出来。Nebulyu 下机看了一下发现用 0 就能很傻逼地凑出来了,甚喜,交了一发发现挂了。又看了眼题发现是不能有 0,三个人一个也没看见,属于是非常低级的错误。
然后 Nebulyu 就去推 D 了,我和 dztle 一块想 J 的交互,发现能确定一条边的存在,再用三次操作就做完了,正好题目限制交互次数不超过 次,感觉很有戏。一开始想了个随机算法,随机 条边检查是否存在,推了下成功的概率感觉很可行,于是开写开调,过样例交了一发但是挂了。
然后发现题目里说交互器是自适应的,上面那个随机算法的成功概率是在交互器不捣乱情况下才成立的,一时不知所措。突然 dztle 灵光一闪,直接检查所有边 就行了,因为这样能保证枚举到所有节点,如果是菊花的话这些边中有且仅有一条是存在的,否则肯定是链。于是开写开调,过样例交了一发但是挂了。我去看了一眼发现多打了个等号,又检查了十几分钟交了一发过了,属于是非常低级的错误。
D
时间回到 Nebulyu 写 M 我和 dztle 推 D 的时候。
首先我看到第二个式子一堆乘号,于是想到这玩意乘起来会 tama 的超级大于是这一坨应该都是 1,按照这个思路推了一下发现不太行。
D 挂了一发 Nebulyu 下机之后我告诉了他这个思路,于是他继续按照这个思路推,发现这东西可以化成一个方程的样子,但是推了推发现只能解决 为奇数的某些情况,没什么通用性,于是只得放弃考虑其他思路。
J 过了之后 dztle 下机也按照这个思路推了一下,发现推出来一个很简单的东西。告诉 Nebulyu 之后一开始还很吃惊因为这条路一开始没走通,讨论了下之后发现确实是对的,迷惑!于是上机写了一发过了,感觉莫名奇妙,令人感慨。
这期间我在干什么呢?我推不出来 D 于是就去把 G 开了呃呃。
G
在 Nebulyu 推 D dztle 写 J 的时候我去开了 G,因为有断尾的操作感觉像是个需要大力优化状态的难写的 bfs。
手玩了下大胆猜了个结论,可以把先断尾再移动使得不撞到自己身子,等价成到自己这节身子的距离不小于断的只剩这节身子的断尾次数,于是在 bfs 的过程中要到自己身子的时候对距离取个最大值就行,感觉对的一批。
然后 Nebulyu 上去把 D 写完了,下来之后跟他讨论了下也感觉这个思路对的一批,于是上机开写。
写的过程中觉得只是 bfs 的话可能会挂于是改成了优先队列但复杂度也是对的,一发过了,本场最舒服的一题。
H
在我写 G 的时候决定继续跟榜开 H。
Nebulyu 和 dztle 发现这东西是个基环内向森林,如果只是棵树的话是非常好做的,有一个简单 DP,但是有环出现自身依赖的时候会比较麻烦。
在想怎么断环的时候 Nebulyu 灵光一闪发现由于有严格小于的限制,这个环里面肯定有某些边是无用的,即这些边代表的操作是绝对无法成立的。于是可以考虑直接把这些边断掉,把环分成若干条链分别考虑,处理完环之后再处理环上连的树。
于是开写开调,一发过了,本场第二舒服的题。
H 之后的一个半小时
看了看榜,除掉打星八十多名,和合肥一样。
是了,我们现在已经稳银了。
是了,再过一题是非常有望冲金的了。
是了,所以要再过哪题呢?
在我写 G 的时候 dztle 看了 A 发现非常可做,就是个大力分类讨论模拟但是难写,看了下榜发现就过了一个所以没敢上机。
在 Nebulyu 写 H 的时候和 dztle 讨论了下 E 和 F 都没有明确的做法,难受。
Nebulyu 下机之后看了下 F,出了一个树分块的带根号的写法,因为这时候 A 还是没什么人过不敢写,觉得也没什么事儿干了还剩一个小时写一发 F 说不定能过,于是上机开写。然而用换根写完调完预处理的一半就还剩 20min 了,寄。
这个时候一直把 dztle 晾在一边挂机,把会了的题放在一边来写复杂度假的东西,可谓是非常可悲的失败。
唉。
赛后
2024.1.26 飞舞唐氏一个回来补题了。
F
才知道题名就是树分块的意思妈的,但是这题数据范围把树分块卡掉了哈哈。
对于此题非常重要的典中典之直径结论:距离树上某点最远的点一定是直径的端点。
发现对于每次询问答案具有单调性,考虑二分枚举 并检查 距离点权值 的点是否均小于等于 。
这东西一眼很不可检查的样子,但是可以考虑构建一棵包含点权值 的最小的子树,即按顺序枚举点权值 对应节点,并将它与之前节点构成的连通块通过唯一的路径连接,则上述检查等价于检查 到子树中所有节点距离是否不大于 。
虽然 不一定在这棵子树上,但是由结论+手玩下可知距离 最远的点还是子树直径的端点,仅需检查 与直接两端点的距离是否不大于 即可。
于是考虑先预处理权值为 的节点组成的子树的直径,按顺序枚举节点并考虑加入对直径的影响,由上述结论可知,新直径要么不变,要么只是一端被替换成新加入的节点,讨论即可维护。
注意这题每次二分答案检查时都需要求距离,若查询距离的复杂度是 则总时间复杂度是 级别的,过不去。则必须用 RMQ 地求两点距离。
则总时间复杂度 级别。
这辈子第一次见到不得不用 RMQ 求 lca 的情况、、、
复制复制// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long #define pr std::pair #define mp std::make_pair const int kN = 5e5 + 10; //============================================================= int n, q, a[kN]; int edgenum, head[kN], v[kN << 1], w[kN << 1], ne[kN << 1]; int fa[kN], sz[kN], dep[kN], son[kN], top[kN]; LL dis[kN], dd[kN]; int pos[kN], maxans; pr <int, int> d[kN]; //============================================================= inline LL read() { LL f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3ll) + (w << 1ll) + (ch ^ '0'); return f * w; } void Add(int u_, int v_, int w_) { v[++ edgenum] = v_; w[edgenum] = w_; ne[edgenum] = head[u_]; head[u_] = edgenum; } namespace ST { int num, Log2[kN << 1], f[kN << 1][20], fir[kN]; void Dfs(int u_, int fa_) { dep[u_] = dep[fa_] + 1; fir[u_] = ++ num; f[num][0] = u_; for (int i = head[u_]; i; i = ne[i]) { int v_ = v[i], w_ = w[i]; if (v_ == fa_) continue ; dis[v_] = dis[u_] + 1ll * w_; Dfs(v_, u_); f[++ num][0] = u_; } } void Prepare() { Dfs(1, 1); Log2[1] = 0; for (int i = 2; i <= num; ++ i) { Log2[i] = Log2[i >> 1] + 1; } for (int i = 1; i <= 19; ++ i) { for (int j = 1; j + (1 << i) - 1 <= num; ++ j) { if (dep[f[j][i - 1]] < dep[f[j + (1 << (i - 1))][i - 1]]) { f[j][i] = f[j][i - 1]; } else { f[j][i] = f[j + (1 << (i - 1))][i - 1]; } } } } int Lca(int u_, int v_) { int l = fir[u_], r = fir[v_]; if (l > r) std::swap(l, r); int lth = Log2[r - l + 1]; if (dep[f[l][lth]] < dep[f[r - (1 << lth) + 1][lth]]) { return f[l][lth]; } return f[r - (1 << lth) + 1][lth]; } LL Dis(int u_, int v_) { return dis[u_] + dis[v_] - 2ll * dis[Lca(u_, v_)]; } } void Init() { n = read(), q = read(); for (int i = 1; i <= n; ++ i) { a[i] = read(); if (a[i] <= n) pos[a[i]] = i; } for (int i = 1; i < n; ++ i) { int u_ = read(), v_ = read(), w_ = read(); Add(u_, v_, w_), Add(v_, u_, w_); } ST::Prepare(); for (int i = 0; i <= n; ++ i) { if (!pos[i]) { maxans = i; break; } if (i == 0) { d[i] = mp(pos[i], pos[i]); dd[i] = 0; } else if (i == 1) { d[i] = mp(pos[0], pos[1]); dd[i] = ST::Dis(pos[0], pos[1]); } else { LL d1 = dd[i - 1]; LL d2 = ST::Dis(pos[i], d[i - 1].first); LL d3 = ST::Dis(pos[i], d[i - 1].second); dd[i] = std::max(d1, std::max(d2, d3)); if (dd[i] == d1) d[i] = d[i - 1]; if (dd[i] == d2) d[i] = mp(d[i - 1].first, pos[i]); if (dd[i] == d3) d[i] = mp(d[i - 1].second, pos[i]); } } } bool check(int x_, LL k_, int mid_) { LL d1 = ST::Dis(x_, d[mid_].first); LL d2 = ST::Dis(x_, d[mid_].second); return d1 <= k_ && d2 <= k_; } int Query(int x_, LL k_) { int ans = maxans; for (int l = 0, r = maxans - 1; l <= r; ) { int mid = (l + r) >> 1; if (check(x_, k_, mid)) { l = mid + 1; } else { ans = mid; r = mid - 1; } } return ans; } //============================================================= int main() { Init(); for (int i = 1; i <= q; ++ i) { int x = read(); LL k = read(); printf("%d\n", Query(x, k)); } return 0; }
写在最后
然后是谢罪环节。这场从各种意义上都是打得稀烂,最后能把 5 题凑出来守银也算是谢天谢地了。
- 开局状态很垃圾,卡签到题上卡太久了呃呃,D 没看限制硬送一发,J 多打一个等号硬送一发,快两个小时才把所有签到签完。
- 中期两个银牌题过得还不是太烂,都是直接想到正解调了调一发过了没掉到铜区,谢天谢地。
- 后面还剩一个半小时,A 的思路从三个小时的时候就已经出来了,而且复杂度也是对的,但是难写,而且当时只有一个队过了这题,于是宁愿跟榜做 F 写带根号的算法也不敢写,从赛后来看是非常失败的决定。
虽然对于我个人来说,因为有合肥时看了一眼 K 没人过于是转头写 G 赛后发现 K 确实是牛逼题的经历,这次赛时选择一直跟榜倒也是正常的。
dztle 说的对,因为金牌区的神仙随便一道金牌题都会写只是先开到哪道的区别,所以金牌题歪榜的概率极大。当时合肥 I 看起来是牛逼题其实只是个大力爆搜剪枝,金牌爷们都去做 B 了所以过的人不多。我们没有稳金的实力,要想冲金只能靠手气运气和胆量了,靠跟榜就只能保证稳定守银罢了。
为什么会变成这样呢?大概是因为我和 Nebulyu 心态被这学期折磨得爆炸了,超负荷运作了,从合肥到杭州这两周基本没训练都在忙各种屁事,这场从一开始就只是打算能保证稳定守个银算了。
唉。
好想死。
另外感觉自己整场除了把 G 切了一直在睡大觉屁用没有,太废物了,已紫砂。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】