A - Array
题目描述
给定一个长度为 NNN 的序列 A=(A1,A2,⋯ ,AN)A = (A_1, A_2, \cdots , A_N)A=(A1,A2,⋯,AN)。
随后给定一个在 111 到 NNN 之间(包括端点)的整数 XXX。
输出 AXA_XAX 的值。
解题思路
题目要求输出序列 AAA 中第 XXX 个元素的值,而题目已经保证 1≤X≤N1 \leq X \leq N1≤X≤N,所以我们只需要把数组存起来,然后直接按下标访问即可。注意到题目中数组的下标是从 111 开始的(A1A_1A1 表示第一个元素),因此在实现时我们将数组的下标也设为从 111 开始,这样就能和题目描述一一对应,不需要额外的下标转换操作。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 105;
int a[maxn];
int main()
{
int n;
cin >> n;
// 读取数组元素,下标从 1 开始以匹配题目描述
for (int i = 1; i <= n; i++)
cin >> a[i];
// 读取要查询的下标 X
int x;
cin >> x;
// 直接通过下标访问并输出对应元素
cout << a[x];
return 0;
}
B - Arrays
题目描述
给定 NNN 个序列 A1,A2,...,ANA_1, A_2, \ldots, A_NA1,A2,...,AN。
序列 AiA_iAi 的长度为 LiL_iLi,且 Ai=(Ai,1,Ai,2,...,Ai,Li)A_i = (A_{i,1}, A_{i,2}, \ldots, A_{i,L_i})Ai=(Ai,1,Ai,2,...,Ai,Li)。
随后给定整数 XXX 和 YYY,输出 AX,YA_{X,Y}AX,Y 的值。
解题思路
本题是 A 题的二维扩展,由一维数组变成了多个一维数组。观察到 YYY 是从 000 开始计数的(Ai,0A_{i,0}Ai,0 表示第一个元素),而 C++ 中的 vector 下标也是从 000 开始,因此我们直接将输入存入 vector 中,然后通过 a[X][Y-1] 来访问目标元素(因为 YYY 需要减一才能对应到 C++ 的下标)。
cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
vector<vector<int>> a;
int main()
{
int n;
cin >> n;
// 调整 a 的大小以存储 N 个序列
a.resize(n + 1);
// 分别读取每个序列的元素
for (int i = 1; i <= n; i++)
{
int l;
cin >> l;
for (int j = 0; j < l; j++)
{
int x;
cin >> x;
a[i].push_back(x);
}
}
// 读取要查询的序列编号 X 和元素下标 Y
int x, y;
cin >> x >> y;
// 由于题目中 Y 从 0 开始计数,需要减一转换为 C++ 的下标
cout << a[x][y - 1];
return 0;
}
C - Long Sequence
题目描述
给定整数 NNN 和 KKK,以及 NNN 个整数序列 A1,A2,...,ANA_1, A_2, \ldots, A_NA1,A2,...,AN 和长度为 NNN 的整数序列 C=(C1,C2,...,CN)C = (C_1, C_2, \ldots, C_N)C=(C1,C2,...,CN)。序列 AiA_iAi 的长度为 LiL_iLi,且 Ai=(Ai,1,Ai,2,...,Ai,Li)A_i = (A_{i,1}, A_{i,2}, \ldots, A_{i,L_i})Ai=(Ai,1,Ai,2,...,Ai,Li)。题目保证 1≤K≤∑i=1NCiLi1 \le K \le \sum_{i=1}^N C_iL_i1≤K≤∑i=1NCiLi。
按照以下过程从 AAA 和 CCC 构造整数序列 B=(B1,B2,...,B∑i=1NCiLi)B = (B_1, B_2, \ldots, B_{\sum_{i=1}^N C_iL_i})B=(B1,B2,...,B∑i=1NCiLi):
- 令 BBB 为长度为 000 的整数序列。
- 对于 i=1,2,...,Ni = 1, 2, \ldots, Ni=1,2,...,N 按顺序执行以下操作:
- 执行 CiC_iCi 次:将 AiA_iAi 追加到 BBB 的末尾。
求出 BKB_KBK 的值。
解题思路
本题如果真的构造出整个序列 BBB 会超出时间限制,因为 BBB 的长度可能达到 101310^{13}1013 级别。关键在于观察:BBB 是由各个 AiA_iAi 依次重复 CiC_iCi 次拼接而成的,因此我们只需要定位 BKB_KBK 落在哪个 AiA_iAi 中即可。
具体做法是从第一个序列开始遍历,维护一个变量 cur 指向当前处理的序列。设当前序列 AcurA_{cur}Acur 的长度为 LLL,重复次数为 CcurC_{cur}Ccur,则该序列在 BBB 中占据的长度为 L×CcurL \times C_{cur}L×Ccur。如果 KKK 小于等于这个长度,说明答案就在当前序列中,直接输出即可;如果 KKK 更大,则将 KKK 减去这个长度,继续处理下一个序列。
由于 AiA_iAi 中的元素是依次重复的,BKB_KBK 在 AiA_iAi 中的位置可以通过 (K−1) mod Li(K-1) \bmod L_i(K−1)modLi 来计算。遍历的复杂度为 O(N)O(N)O(N),时间上完全可行。
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
vector<vector<int>> a;
vector<int> b, c;
int n, k;
signed main()
{
cin >> n >> k;
// 调整容器大小以存储 N 个序列
a.resize(n);
// 读取各个序列 A_i 的元素
for (int i = 0; i < n; i++)
{
int l;
cin >> l;
for (int j = 1; j <= l; j++)
{
int x;
cin >> x;
a[i].push_back(x);
}
}
// 读取序列 C,记录每个 A_i 需要重复的次数
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
c.push_back(x);
}
// 从第一个序列开始遍历,定位 B_K 落在哪个序列中
int cur = 0;
while (k && cur < n)
{
// 当前序列在 B 中占据的长度
if (k <= a[cur].size() * c[cur])
{
// 答案在当前序列中,通过取模确定具体位置
cout << a[cur][(k - 1) % a[cur].size()];
return 0;
}
// K 不在当前序列,减去当前序列占据的长度后继续向后查找
k -= a[cur].size() * c[cur];
cur++;
}
return 0;
}
D - Raise Minimum
题目描述
给定一个长度为 NNN 的序列 A=(A1,A2,...,AN)A = (A_1, A_2, \ldots, A_N)A=(A1,A2,...,AN) 和整数 KKK。
你可以执行 000 到 KKK 次(包括端点)以下操作:
- 选择满足 1≤i≤N1 \le i \le N1≤i≤N 的整数 iii,并将 iii 加到 AiA_iAi 上。
求执行操作后序列的 min1≤i≤NAi\displaystyle \min_{1 \le i \le N} A_i1≤i≤NminAi 的最大可能值。
解题思路
本题要求通过若干次操作使得序列的最小值最大化。观察操作的特点:每次选择位置 iii,可以将 AiA_iAi 增加 iii。这意味着位置 iii 每次增加的量是固定的 iii,而且我们最多执行 KKK 次操作。
对于给定的目标值 xxx,我们可以检查是否能够在 KKK 次操作内将所有元素提升到至少 xxx。对于位置 iii,如果 Ai<xA_i < xAi<x,需要增加 x−Aix - A_ix−Ai 才能达到目标。由于每次操作最多增加 iii,因此至少需要 ⌈(x−Ai)/i⌉\lceil (x - A_i) / i \rceil⌈(x−Ai)/i⌉ 次操作才能将 AiA_iAi 提升到 xxx。对所有位置求和得到总操作次数,如果不超过 KKK 则说明 xxx 是可达的。
基于上述判断函数,我们可以对 xxx 进行二分搜索。搜索的下界可以是 111,上界则可以是 A1+K+1A_1 + K + 1A1+K+1(最极端情况下全给 A1A_1A1 增加值)。每次取中点检查,如果可达则左边界移动,否则右边界移动,直到左右边界相差 111 为止。最终左边界即为最小值的最大可能值。使用 __int128 是因为计算过程中乘积可能超出 long long 范围。
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, k;
vector<int> A;
// 检查是否能在 K 次操作内将所有元素提升到至少 x
bool check(int x)
{
__int128 cnt = 0;
for (int i = 1; i <= n; i++)
{
if (A[i] < x)
{
// 需要 (x - A[i]) / i 次操作,向上取整
cnt += ( (__int128)x - A[i] + i - 1 ) / i;
// 如果已超过 K 次则直接返回 false
if (cnt > k) return false;
}
}
return cnt <= k;
}
signed main()
{
cin >> n >> k;
A.resize(n + 1);
for (int i = 1; i <= n; i++)
cin >> A[i];
// 二分搜索上界,最极端情况下全给 A[1] 增加值
__int128 right = (__int128)A[1] + k + 1;
int left = 1;
while (right - left > 1)
{
int mid = (left + right) / 2;
if (check(mid))
left = mid;
else
right = mid;
}
cout << left << endl;
return 0;
}
E - Crossing Table Cloth
题目描述
有 NNN 个单元格排成一行。从左数第 iii 个单元格 (1≤i≤N)(1 \le i \le N)(1≤i≤N) 称为单元格 iii。
有 MMM 块布。第 iii 块布 (1≤i≤M)(1 \le i \le M)(1≤i≤M) 覆盖从 LiL_iLi 到 RiR_iRi 的单元格。
回答 QQQ 个查询。对于第 qqq 个查询 (1≤q≤Q)(1 \le q \le Q)(1≤q≤Q),给定整数 SqS_qSq 和 TqT_qTq,请回答以下问题:
- 判断是否可以从 MMM 块布中恰好选择两块并铺设,使得满足以下条件:
- 单元格 SqS_qSq 到 TqT_qTq 被至少一块布覆盖,且没有其他单元格被任何布覆盖。
解题思路
本题要求判断是否能用恰好两块布完全覆盖指定区间 [Sq,Tq][S_q, T_q][Sq,Tq]。关键在于理解"恰好两块布"和"完全覆盖"这两个条件。
首先,如果存在完全相同的 [Li,Ri][L_i, R_i][Li,Ri] 等于 [L,R][L, R][L,R] 的布至少两块,那么显然可以用这两块布覆盖目标区间。如果只有一块完全相同的布,我们需要寻找其他方案。
考虑将覆盖区间分为左右两部分:让一块布负责覆盖区间的左半部分,另一块布负责右半部分。两块布的覆盖范围在中间可以重叠一个单元格,但不能再有其他空隙或延伸到区间之外。因此,我们需要找到一块以 LLL 为左端点的布,以及一块以 RRR 为右端点的布,使得它们的覆盖范围能够恰好(可以重叠)覆盖 [L,R][L, R][L,R]。
具体实现时,我们预处理两组信息:l[i] 记录所有左端点为 iii 的布的右端点集合,r[i] 记录所有右端点为 iii 的布的左端点集合。对于每个查询,先检查是否满足两块完全相同的情况,然后检查是否存在可以分割覆盖的方案。分割覆盖的条件是:存在一块以 LLL 为左端点的布,其右端点至少为 R−1R-1R−1,同时存在一块以 RRR 为右端点的布,其左端点至多为 L+1L+1L+1。
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
// l[i] 存储所有左端点为 i 的布的右端点
// r[i] 存储所有右端点为 i 的布的左端点
vector<vector<int>> l(n + 2);
vector<vector<int>> r(n + 2);
// cnt 用于计数完全相同的布的数量
map<pair<int, int>, int> cnt;
vector<pair<int, int>> lr;
// 读取每块布的信息
for (int i = 0; i < m; ++i)
{
int L, R;
cin >> L >> R;
l[L].push_back(R);
r[R].push_back(L);
lr.emplace_back(L, R);
cnt[{L, R}]++;
}
// 对 l 和 r 中的右端点/左端点集合进行排序,便于二分查找
for (int i = 1; i <= n; ++i)
{
sort(l[i].begin(), l[i].end());
sort(r[i].begin(), r[i].end());
}
sort(lr.begin(), lr.end());
// mi[i] 记录从位置 i 开始的最小的右端点
vector<int> mi(n + 2, (int)1e9);
int now = (int)1e9;
for (int i = m - 1; i >= 0; --i)
{
mi[lr[i].first] = min(now, lr[i].second);
now = mi[lr[i].first];
}
// 从后向前递推,确保 mi[i] 是从 i 到 n 的最小右端点
for (int i = n - 1; i >= 1; --i)
{
mi[i] = min(mi[i], mi[i + 1]);
}
int q;
cin >> q;
while (q--)
{
int L, R;
cin >> L >> R;
// 如果有两块完全相同的布,可以直接覆盖
if (cnt[{L, R}] >= 2)
{
cout << "Yes\n";
continue;
}
if (L >= n)
{
cout << "No\n";
continue;
}
// 检查是否存在分割覆盖的方案
if (cnt[{L, R}] == 1 && (mi[L] < R || mi[L + 1] <= R))
{
cout << "Yes\n";
continue;
}
// 查找以 L 为左端点的布中,右端点最大的那块
bool l_is = !l[L].empty();
int l_max = -1;
if (l_is)
{
auto it = lower_bound(l[L].begin(), l[L].end(), R);
if (it != l[L].begin())
{
--it;
l_max = *it;
}
else
{
l_max = -1;
}
}
// 查找以 R 为右端点的布中,左端点最小的那块
bool r_is = !r[R].empty();
int r_min = (int)1e9;
if (r_is)
{
auto it = upper_bound(r[R].begin(), r[R].end(), L);
if (it != r[R].end())
{
r_min = *it;
}
else
{
r_min = (int)1e9;
}
}
// 判断两块布是否能覆盖 [L, R] 区间(允许重叠一个单元格)
if (l_is && r_is && l_max >= r_min - 1)
{
cout << "Yes\n";
}
else
{
cout << "No\n";
}
}
return 0;
}
F - Second Gap
题目描述
给定一个整数 NNN 和一个长度为 N−1N-1N−1 的整数序列 D=(D1,D2,...,DN−1)D = (D_1, D_2, \ldots, D_{N-1})D=(D1,D2,...,DN−1)。
求满足以下条件的排列 P=(P1,P2,...,PN)P = (P_1, P_2, \ldots, P_N)P=(P1,P2,...,PN)(1,2,...,N1, 2, \ldots, N1,2,...,N 的排列)的数量,模 998244353998244353998244353:
- 对于每个 1≤i≤N−11 \le i \le N-11≤i≤N−1,设 PaP_aPa 和 PbP_bPb 为 (Pi,Pi+1,...,PN)(P_i, P_{i+1}, \ldots, P_N)(Pi,Pi+1,...,PN) 中最大和第二大的元素,则 ∣a−b∣=Di|a - b| = D_i∣a−b∣=Di。
解题思路
本题要求构造一个排列,使得后缀中最大和第二大元素的下标距离恰好等于对应的 DiD_iDi。从后向前考虑会更加方便,因为当我们处理位置 iii 时,位置 i+1i+1i+1 到 NNN 的排列已经确定。
从后往前递推时,第 iii 个位置需要满足 ∣i−(i+Di)∣=Di|i - (i + D_i)| = D_i∣i−(i+Di)∣=Di 这个条件,这意味着第 iii 个位置的最大值必须放在 i+Dii + D_ii+Di 处。设 valvalval 为位置 i+Dii + D_ii+Di 处的值,我们需要将 valvalval 放到位置 iii 作为后缀的最大值,然后将 111 到 val−1val-1val−1 这些较小的值分配到其他位置。
具体实现使用线段树来维护每个位置可以放置的值的数量。对于每个位置 iii,我们需要计算从 iii 到 nnn 这个后缀中已经放置的最大值 valvalval,然后根据 DiD_iDi 和 Di+1D_{i+1}Di+1 的关系来决定如何更新其他位置的可选值数量。当 Di≠Di+1D_i \neq D_{i+1}Di=Di+1 时,其他所有位置都只能放置比 valvalval 小的值;当 Di=Di+1D_i = D_{i+1}Di=Di+1 时,除了 iii 和 i+Dii + D_ii+Di 之外的位置可以放置 111 到 val−1val-1val−1 中的任意值。线段树支持区间赋值、区间乘法加法以及单点查询等操作,从而高效地完成动态规划的维护。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MOD 998244353
const int maxn = 200005;
// 线段树节点标记:tree存储区间和,add为加法标记,mul为乘法标记,edt为赋值标记(-1表示无赋值)
ll tree[4 * maxn];
ll add[4 * maxn];
ll mul[4 * maxn];
ll edt[4 * maxn];
int di[maxn];
int n;
// 向下推送懒标记,确保子区间的值正确
void push(int node, int l, int r)
{
if (edt[node] != -1)
{
int mid = (l + r) / 2;
int left = 2 * node, right_node = 2 * node + 1;
tree[left] = edt[node] * (mid - l + 1) % MOD;
edt[left] = edt[node];
add[left] = 0;
mul[left] = 1;
tree[right_node] = edt[node] * (r - mid) % MOD;
edt[right_node] = edt[node];
add[right_node] = 0;
mul[right_node] = 1;
edt[node] = -1;
}
if (mul[node] != 1 || add[node] != 0)
{
int mid = (l + r) / 2;
int left = 2 * node, right_node = 2 * node + 1;
tree[left] = (tree[left] * mul[node] + add[node] * (mid - l + 1)) % MOD;
add[left] = (add[left] * mul[node] + add[node]) % MOD;
mul[left] = mul[left] * mul[node] % MOD;
tree[right_node] = (tree[right_node] * mul[node] + add[node] * (r - mid)) % MOD;
add[right_node] = (add[right_node] * mul[node] + add[node]) % MOD;
mul[right_node] = mul[right_node] * mul[node] % MOD;
mul[node] = 1;
add[node] = 0;
}
}
// 区间更新操作,支持加法、乘法和赋值三种标记
void update_range(int node, int l, int r, int ul, int ur, ll add_val, ll mul_val, ll edt_val)
{
if (ur < l || ul > r)
return;
if (ul <= l && r <= ur)
{
if (edt_val != -1)
{
tree[node] = edt_val * (r - l + 1) % MOD;
edt[node] = edt_val;
add[node] = 0;
mul[node] = 1;
}
else
{
tree[node] = (tree[node] * mul_val + add_val * (r - l + 1)) % MOD;
add[node] = (add[node] * mul_val + add_val) % MOD;
mul[node] = mul[node] * mul_val % MOD;
}
return;
}
push(node, l, r);
int mid = (l + r) / 2;
update_range(2 * node, l, mid, ul, ur, add_val, mul_val, edt_val);
update_range(2 * node + 1, mid + 1, r, ul, ur, add_val, mul_val, edt_val);
tree[node] = (tree[2 * node] + tree[2 * node + 1]) % MOD;
}
// 单点查询,返回指定位置的当前值
ll query_pos(int node, int l, int r, int pos)
{
if (l == r)
return tree[node];
push(node, l, r);
int mid = (l + r) / 2;
if (pos <= mid)
return query_pos(2 * node, l, mid, pos);
return query_pos(2 * node + 1, mid + 1, r, pos);
}
// 区间求和查询
ll query_sum(int node, int l, int r, int ql, int qr)
{
if (qr < l || ql > r)
return 0;
if (ql <= l && r <= qr)
return tree[node];
push(node, l, r);
int mid = (l + r) / 2;
return (query_sum(2 * node, l, mid, ql, qr) + query_sum(2 * node + 1, mid + 1, r, ql, qr)) % MOD;
}
// 初始化线段树,末尾两个位置初始化为1(表示可以选择值1)
void init()
{
for (int i = 0; i < 4 * maxn; ++i)
{
tree[i] = 0;
add[i] = 0;
mul[i] = 1;
edt[i] = -1;
}
update_range(1, 1, n, n - 1, n - 1, 1, 1, -1);
update_range(1, 1, n, n, n, 1, 1, -1);
}
// 从后向前递推,根据 D_i 和 D_{i+1} 的关系更新各位置的可选值数量
void solve()
{
scanf("%d", &n);
for (int i = 1; i < n; ++i)
{
scanf("%d", &di[i]);
}
init();
for (int i = n - 2; i >= 1; --i)
{
// 取出位置 i + D_i 处的值作为当前后缀的最大值
ll val = query_pos(1, 1, n, i + di[i]);
if (di[i] != di[i + 1])
{
// D_i 与 D_{i+1} 不同时,其他位置只能放置比 val 小的值
update_range(1, 1, n, 1, n, 0, 1, 0);
}
else
{
// D_i 与 D_{i+1} 相同时,根据 n-i-1 的值更新
update_range(1, 1, n, 1, n, 0, (ll)(n - i - 1), -1);
}
// 将位置 i 和 i + D_i 固定为 val
update_range(1, 1, n, i, i, val, 1, -1);
update_range(1, 1, n, i + di[i], i + di[i], val, 1, -1);
}
// 输出所有位置的可选值数量之和
printf("%lld\n", query_sum(1, 1, n, 1, n) % MOD);
}
int main()
{
solve();
return 0;
}
G - Catch All Apples
题目描述
有 NNN 个苹果落在数轴上。第 iii 个苹果在时间 TiT_iTi 落在坐标 XiX_iXi 处。
你可以在数轴上的任意位置放置一些机器人来收集所有 NNN 个苹果。机器人可以放置在任意坐标。
每个机器人从时间 000 开始运行,可以以速度至多 111 在数轴上自由移动。多个机器人可以在同一时刻占据相同坐标。如果机器人在时间 TiT_iTi 恰好处于坐标 XiX_iXi,则它可以收集第 iii 个苹果。
求收集所有苹果所需的最少机器人数量。
解题思路
本题要求计算机器人收集所有苹果所需的最少数量,关键在于将苹果的时空信息进行转换。对于每个苹果 (Ti,Xi)(T_i, X_i)(Ti,Xi),我们考虑两个组合:Ti+XiT_i + X_iTi+Xi 和 Ti−XiT_i - X_iTi−Xi。前者表示如果一直向右走能到达的最远距离,后者表示如果一直向左走能到达的最远距离。
观察发现,一个机器人如果在时间 TiT_iTi 处于 XiX_iXi,那么它必须满足 ∣Xi−x0∣≤Ti|X_i - x_0| \le T_i∣Xi−x0∣≤Ti,其中 x0x_0x0 是机器人的起始位置。这意味着机器人收集的苹果序列必须满足某种偏序关系。
我们按 Ti+XiT_i + X_iTi+Xi 从大到小对苹果排序,然后计算 Ti−XiT_i - X_iTi−Xi 的最长不上升子序列长度。具体来说,排序后我们需要找的是 Ti−XiT_i - X_iTi−Xi 的最长不下降子序列。设 d=Ti−Xid = T_i - X_id=Ti−Xi,问题转化为在序列 ddd 中找到一个最长的子序列,使得这个子序列单调不下降。这个子序列的长度就是所需的最少机器人数量。
为什么这样是对的呢?因为在按 Ti+XiT_i + X_iTi+Xi 排序后,对于子序列中的任意两个苹果 (Ta,Xa)(T_a, X_a)(Ta,Xa) 和 (Tb,Xb)(T_b, X_b)(Tb,Xb)(aaa 在 bbb 前面),如果 Ta−Xa≤Tb−XbT_a - X_a \le T_b - X_bTa−Xa≤Tb−Xb,那么机器人可以在收集完苹果 aaa 后赶到苹果 bbb 的位置。这正好满足机器人速度为 111 的约束。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll INF = 1e18;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll n;
cin >> n;
// vals[i][0] = t + x, vals[i][1] = t - x
vector<array<ll, 2>> vals(n);
for (ll i = 0; i < n; ++i)
{
ll t, x;
cin >> t >> x;
vals[i] = {t + x, t - x};
}
// 按 t + x 从大到小排序,t - x 从大到小排序
sort(vals.begin(), vals.end(), [&](array<ll, 2> a, array<ll, 2> b)
{
if (a[0] == b[0]) return a[1] > b[1];
return a[0] > b[0]; });
// dp 数组用于维护最长不下降子序列,dp[i] 表示长度为 i+1 的子序列的最小尾值
vector<ll> dp(n + 1, INF);
dp[0] = -INF;
ll res = 1;
for (ll i = 0; i < n; ++i)
{
ll d = vals[i][1];
// 找到第一个大于等于 d 的位置,即为可接续的位置
ll ind = lower_bound(dp.begin(), dp.end(), d) - dp.begin();
dp[ind] = min(dp[ind], d);
res = max(res, ind);
}
cout << res << "\n";
return 0;
}