ABC457-二分答案/偏序集+最长上升子序列/数据结构+二分查找
ABC457
Tasks - Polaris.AI Programming Contest 2026(AtCoder Beginner Contest 457)
首次破D且运气很好做过类似的G,排名最高的一把(差点被关在空教室回不来)
我终于会折叠标题了!!!
A - 模拟
简单模拟.
n = int(input())a = list(map(int,input().split()))x = int(input())print(a[x-1])B - 模拟
比A好理解的模拟(我是不会告诉你赛时A我看了2分钟不知道没看出来要求属什么,甚至以为是递增序列的)
n = int(input())g = []for _ in range(n): t = list(map(int,input().split())) g.extend([t[1:]])x,y = map(int,input().split())print(g[x-1][y-1])C - 模拟
简单模拟,值得注意的是k的取值会非常大,因此直接存会RE
判断k是否大于当前序列ai在b中的总长度,如果小于,说明要找的目标在后面的序列,k-cur
否则,目标值在当前ai的重复块中,(k-1) % L即可求得目标
n, k = map(int, input().split())g = []for _ in range(n): t = list(map(int, input().split())) g.append(t[1:])c = list(map(int, input().split()))for i in range(n): cur = len(g[i]) * c[i] if k > cur: k -= cur else: print(g[i][(k - 1) % len(g[i])]) breakD - 二分答案
二分答案是一中求最小的最大可能值的很有效的方法
check()函数的作用用于判断经操作后数组中的所有值能否大于mid
c++在处理本题时要注意数据的范围非常大
n, k = map(int, input().split())a = list(map(int, input().split()))def check(mid): ops = 0 for i, x in enumerate(a): if x < mid: t = (mid - x + i) // (i + 1) ops += t if ops > k: return False return True
left = min(a) - 1right = max(a) + k * n + 1while left + 1 < right: mid = (left + right) // 2 if check(mid): left = mid else: right = midprint(left)#include<bits/stdc++.h>using namespace std;typedef long long ll;using i64 = long long;using u64 = unsigned long long;using u32 = unsigned;using i128 = __int128;using u128 = unsigned __int128;#include <iostream>#include <string>#include <algorithm>using namespace std;
void print_i128(__int128 x) { if (x == 0) { cout << '0'; return; } if (x < 0) { cout << '-'; x = -x; } string s; while (x) { s += char('0' + (int)(x % 10)); x /= 10; } reverse(s.begin(), s.end()); cout << s;}int main(){ i64 n,k; cin >> n >> k; vector<i64> a(n); for(int i = 0;i < n ;i++){ cin >> a[i]; }
auto check = [&](i128 mid){ i128 pos = 0; for(int i = 0;i < n;i++){ if(mid > a[i]){ i128 t = (mid - a[i] + i)/(i+1); pos += t; if(pos > k){ return false; } } } return true; };
i128 l = *min_element(a.begin(),a.end()) - 1; i128 r = *max_element(a.begin(),a.end()) + (i128)k*n + 1; while (l + 1 < r){ i128 mid = (l + r)/2; if (check(mid)){ l = mid; } else{ r = mid; } } print_i128(l); cout << endl; return 0;}G - 偏序集 + 最长上升子序列
题意:
我们有 个事件 。
一个机器人可以先后收集两个事件 和 (假设 )当且仅当它们满足“速度约束”:
也就是说,机器人在时间差内最多移动距离不超过时间差。
把这一条件改写,引入变量
则上述条件等价于(设 ):
因此,能被同一个机器人收集的事件,在 坐标系下,必须具有两维都不减小的偏序关系。
题解:
**偏序集与 **Dilworth 定理
我们把每个事件抽象成一个点 ,并定义偏序关系:
我们要找的是最少的单调链,这种链中元素全都可以被同一个机器人按照时间顺序收集
Dilworth 定理:
最小链划分的大小 = 最长反链的大小
反链是集合中任意两点不可比较的集合,即对于任意两点 ,不能同时满足 。
在二维平面上按 坐标排序后,反链在 坐标上一定是严格递减的。
如果 且 ,那么它们就是可比较的(自然可以放在同一条链中),这就不属于反链。
因此,要构成反链,当 递增时, 必须严格递减。
最长反链的长度 = 最长严格递减子序列(** 坐标)的长度**
如果 ,那么除非 (不可能,因为 不同),否则它们 。
如果 相同且 不同,那么它们的 不同,但无论如何,它们的时间 不一定相同。
因此,当 相同时,先按 降序排序,可使相同 的点不会错误地构成一条链,因为这时 递减,不会满足 的要求。
由此得出求最大反链 = 最长严格递减子序列
按 升序、 相同时按 降序排序后,原偏序关系变成:
要两点可比,必须同时满足 递增且 递增(因为相同的 我们已经按 降序安排,它们之间无法再形成 递增)。
于是,任何一个反链在这样的排序下就是 严格递减的子序列。
import sysimport bisectinput = sys.stdin.readlinen = int(input())a = []for _ in range(n): t, x = map(int, input().split()) a.append((x + t, x - t))a.sort(key=lambda p: (p[0], -p[1]))b = [p[1] for p in a]res = []for b in b: pos = bisect.bisect_left(res, b) if pos == len(res): res.append(b) else: res[pos] = bprint(len(res))E - 数据结构 + 二分查找
一道数据结构题,难点在于高效的存储和查询数据(暂未补)
import sysfrom bisect import bisect_right, bisect_leftfrom collections import defaultdictinput = sys.stdin.readlineN, M = map(int, input().split())by_l = [[] for _ in range(N + 1)]by_r = [[] for _ in range(N + 1)]cnt = defaultdict(int)INF = 10**9min_r_at_l = [INF] * (N + 2)for _ in range(M): L, R = map(int, input().split()) by_l[L].append(R) by_r[R].append(L) cnt[(L, R)] += 1 if R < min_r_at_l[L]: min_r_at_l[L] = R
for i in range(1, N + 1): by_l[i].sort() by_r[i].sort()
suf_min_r = [INF] * (N + 3)for i in range(N, 0, -1): suf_min_r[i] = min(suf_min_r[i + 1], min_r_at_l[i])
Q = int(input())ans = []
for _ in range(Q): S, T = map(int, input().split())
if cnt[(S, T)] > 0: ok = False ok |= cnt[(S, T)] >= 2 ok |= suf_min_r[S + 1] <= T ok |= suf_min_r[S] <= T - 1 print("Yes" if ok else "No") continue
ok = False
rs = by_l[S] pos_r = bisect_right(rs, T) - 1
ls = by_r[T] pos_l = bisect_left(ls, S)
if pos_r >= 0 and pos_l < len(ls): r1 = rs[pos_r] l2 = ls[pos_l] ok |= l2 <= r1 + 1 print("Yes" if ok else "No")