ABC457-二分答案/偏序集+最长上升子序列/数据结构+二分查找

1567 字
8 分钟
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])])
break
D - 二分答案

二分答案是一中求最小的最大可能值的很有效的方法

check()函数的作用用于判断经操作后数组中的所有值能否大于mid

c++在处理本题时要注意数据的范围非常大

二分答案-py
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) - 1
right = max(a) + k * n + 1
while left + 1 < right:
mid = (left + right) // 2
if check(mid):
left = mid
else:
right = mid
print(left)
c++
#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 - 偏序集 + 最长上升子序列

题意:

我们有 N N 个事件 (ti,xi)(t_i, x_i)

一个机器人可以先后收集两个事件 (t1,x1)(t_1, x_1)(t2,x2)(t_2, x_2)(假设 t1t2t_1 \le t_2)当且仅当它们满足“速度约束”:

x1x2t2t1|x_1 - x_2| \le t_2 - t_1

也就是说,机器人在时间差内最多移动距离不超过时间差。

把这一条件改写,引入变量

u=x+t,v=xtu = x + t, \quad v = x - t

则上述条件等价于(设 t1t2t_1 \le t_2):

u1u2v1v2u_1 \le u_2 \quad \text{且} \quad v_1 \le v_2

因此,能被同一个机器人收集的事件,在 (u,v)(u, v) 坐标系下,必须具有两维都不减小的偏序关系。


题解:

**偏序集与 **Dilworth 定理

我们把每个事件抽象成一个点 (u,v)(u, v),并定义偏序关系:

P1P2    u1u2 且 v1v2P_1 \le P_2 \iff u_1 \le u_2 \ \text{且}\ v_1 \le v_2

我们要找的是最少的单调链,这种链中元素全都可以被同一个机器人按照时间顺序收集

Dilworth 定理:

最小链划分的大小 = 最长反链的大小

反链是集合中任意两点不可比较的集合,即对于任意两点 P1,P2P_1, P_2,不能同时满足 u1u2 且 v1v2u_1 \le u_2 \ \text{且}\ v_1 \le v_2


在二维平面上按 uu 坐标排序后,反链在 vv 坐标上一定是严格递减的。

如果 u1<u2u_1 < u_2v1v2v_1 \le v_2,那么它们就是可比较的(自然可以放在同一条链中),这就不属于反链。

因此,要构成反链,当 uu 递增时,vv 必须严格递减

最长反链的长度 = 最长严格递减子序列(vv** 坐标)的长度**​

如果 u1=u2u_1 = u_2,那么除非 v1=v2v_1 = v_2(不可能,因为 (t,x)(t,x) 不同),否则它们 v1v2v_1 \neq v_2

如果 x+tx + t 相同且 xtx - t 不同,那么它们的 (t,x)(t, x) 不同,但无论如何,它们的时间 t=(uv)/2t = (u - v)/2 不一定相同。

因此,当 uu 相同时,先按 vv 降序排序,可使相同 uu 的点不会错误地构成一条链,因为这时 vv 递减,不会满足 v1v2v_1 \le v_2 的要求。

由此得出求最大反链 = 最长严格递减子序列

uu 升序、uu 相同时按 vv 降序排序后,原偏序关系变成:

要两点可比,必须同时满足 uu 递增且 vv 递增(因为相同的 uu 我们已经按 vv 降序安排,它们之间无法再形成 vv 递增)。

于是,任何一个反链在这样的排序下就是 vv 严格递减的子序列。


反链+最长递增子序列
import sys
import bisect
input = sys.stdin.readline
n = 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] = b
print(len(res))
E - 数据结构 + 二分查找

一道数据结构题,难点在于高效的存储和查询数据(暂未补)

数据结构
import sys
from bisect import bisect_right, bisect_left
from collections import defaultdict
input = sys.stdin.readline
N, M = map(int, input().split())
by_l = [[] for _ in range(N + 1)]
by_r = [[] for _ in range(N + 1)]
cnt = defaultdict(int)
INF = 10**9
min_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")
ABC457-二分答案/偏序集+最长上升子序列/数据结构+二分查找
http://blog.7a7a68.xyz/posts/abc457/
作者
Waning
发布于
2026-05-09
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Waning
愿你明日如绚丽之花.
公告
还活着.
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
56
分类
8
标签
20
总字数
125,693
运行时长
0
最后活动
0 天前

目录