P9870 [NOIP2023] 双序列拓展 题解
题意:
称某个序列 B={b1,b2,⋯,bn} 是另一个序列 A={a1,a2,⋯,am} 的拓展当且仅当存在正整数序列 L={l1,l2,⋯,lm},将 ai 替换为 li 个 ai 后得到序列 B。例如,
- {1,3,3,3,2,2,2} 是 {1,3,3,2} 的拓展,取 L={1,1,2,3} 或 {1,2,1,3};
- 而 {1,3,3,2} 不是 {1,3,3,3,2} 的拓展,{1,2,3} 不是 {1,3,2} 的拓展。
小 R 给了你两个序列 X 和 Y,他希望你找到 X 的一个长度为 l0=10100 的拓展 F={fi} 以及 Y 的一个长度为 l0 的拓展 G={gi},使得任意 1≤i,j≤l0 都有 (fi−gi)(fj−gj)>0。由于序列太长,你只需要告诉小 R 是否存在这样的两个序列即可。
为了避免你扔硬币蒙混过关,小 R 还给了 q 次额外询问,每次额外询问中小 R 会修改 X 和 Y 中若干元素的值。你需要对每次得到的新的 X 和 Y 都进行上述的判断。
询问之间是独立的,每次询问中涉及的修改均在原始序列上完成。
分析:
(fi−gi)(fj−gj)>0 这个条件可以看作要么 X 的扩展均比 Y 的扩展小,要么大。因此只需要考虑 X 的扩展均比 Y 的扩展小即可。
对于目前在 X 序列中匹配到 i,在 Y 序列中匹配到 j。可以从 (i,j) 转移到 $(i+1,j) \ (x_{i+1}<y_{j}) ,(i,j+1) \ (x_{i} < y_{j+1})$ 以及 (i+1,j+1) (xi+1<yj+1)。简单 dp 一下,时间复杂度为 O(Tnm) 的。
因此可以将题意转换成 Ai,j=[xi<yj]。判断能否从 (1,1) 走到 (n,m)。
特殊性质:
特殊性质:对于每组询问(包括初始询问和额外询问),保证 x1<y1,且 xn 是序列 X 唯一的一个最小值,ym 是序列 Y 唯一的一个最大值。
提示我们可以从最小值最大值来寻找突破口。
显然如果 Xmin<Ymin 时,AXmin,j=1;否则 Ai,Ymin=0(这一列被堵住了,肯定不合法)。
同理 Xmax<Ymax 时,Ai,Ymax=1;否则 AXmax,j=0(这一行被堵住了,肯定不合法)。
那么合法的一个必要条件是 Xmin<Ymin∧Xmax<Ymax。
保证这个条件后,那么最后一行和最后一列均为 1。
分别找到 X,Y 的前 n−1,m−1 的最小值,如果 Xmin<Ymin。

可以走到 Xmin 这一行,再走到终点,这样我们就缩小了边框。Xmax<Ymax 同理。需要注意的是如果两个条件均不满足,就说明一定不合法。
因此可以记 check1(int x, int y)
表示判断 (1,1) 是否能走到第 x 行或第 y 列。
时间复杂度 O(T(n+m))。
一般情况:
实际上一般情况只是说明最开始的 Xmin 与 Ymax 不一定在最后一行和最后一列。我们直接找到 Xmin 与 Ymax,然后就变成了一个左上角和右下角的子问题。再记 check2(int x, int y, int n, int m)
表示目前能走到第 x 行和第 y 列,判断其是否能走到 (n,m)。
使用 check1 去计算左上角,check2 去计算右下角即可。
时间复杂度同样为 O(T(n+m))。
代码:
#include<bits/stdc++.h>
#define N 500005
using namespace std;
int c, n, m, Q;
int X[N], Y[N];
struct node {
int id, val;
}A_pre_max[N], A_pre_min[N], A_suf_max[N], A_suf_min[N], B_pre_max[N], B_pre_min[N], B_suf_max[N], B_suf_min[N];
node add1(node x, node y) {
node z;
z.val = min(x.val, y.val);
if(z.val == x.val) z.id = x.id;
else z.id = y.id;
return z;
}
node add2(node x, node y) {
node z;
z.val = max(x.val, y.val);
if(z.val == x.val) z.id = x.id;
else z.id = y.id;
return z;
}
bool check1(int x, int y) { //判断[1,1]是否能走到第x行或第y列
if(x == 1 || y == 1) return 1;
node xmin = A_pre_min[x - 1], xmax = A_pre_max[x - 1];
node ymin = B_pre_min[y - 1], ymax = B_pre_max[y - 1];
if(xmin.val < ymin.val) return check1(xmin.id, y);
if(xmax.val < ymax.val) return check1(x, ymax.id);
return 0;
}
bool check2(int x, int y, int n, int m) { //目前能走到第x行和第y列,判断其是否能走到[n,m]
if(x == n || y == m) return 1;
node xmin = A_suf_min[x + 1], xmax = A_suf_max[x + 1];
node ymin = B_suf_min[y + 1], ymax = B_suf_max[y + 1];
if(xmin.val < ymin.val) return check2(xmin.id, y, n, m);
if(xmax.val < ymax.val) return check2(x, ymax.id, n, m);
return 0;
}
bool Sol(int n, int m, int X[], int Y[]) {
if(X[1] > Y[1]) return 0;
A_pre_max[1] = A_pre_min[1] = ((node){1, X[1]});
B_pre_max[1] = B_pre_min[1] = ((node){1, Y[1]});
A_suf_max[n] = A_suf_min[n] = ((node){n, X[n]});
B_suf_max[m] = B_suf_min[m] = ((node){m, Y[m]});
for(int i = 2; i <= n; i++) {
A_pre_min[i] = add1(A_pre_min[i - 1], (node){i, X[i]});
A_pre_max[i] = add2(A_pre_max[i - 1], (node){i, X[i]});
}
for(int i = n - 1; i >= 1; i--) {
A_suf_min[i] = add1(A_suf_min[i + 1], (node){i, X[i]});
A_suf_max[i] = add2(A_suf_max[i + 1], (node){i, X[i]});
}
for(int i = 2; i <= m; i++) {
B_pre_min[i] = add1(B_pre_min[i - 1], (node){i, Y[i]});
B_pre_max[i] = add2(B_pre_max[i - 1], (node){i, Y[i]});
}
for(int i = m - 1; i >= 1; i--) {
B_suf_min[i] = add1(B_suf_min[i + 1], (node){i, Y[i]});
B_suf_max[i] = add2(B_suf_max[i + 1], (node){i, Y[i]});
}
if(A_pre_min[n].val >= B_pre_min[m].val || A_pre_max[n].val >= B_pre_max[m].val) return 0;
return check1(A_pre_min[n].id, B_pre_max[m].id) && check2(A_pre_min[n].id, B_pre_max[m].id, n, m);
}
int AA[N], BB[N];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> c >> n >> m >> Q;
for(int i = 1; i <= n; i++) cin >> X[i], AA[i] = X[i];
for(int i = 1; i <= m; i++) cin >> Y[i], BB[i] = Y[i];
cout << (Sol(n, m, X, Y) || Sol(m, n, Y, X));
while(Q--) {
int kx, ky;
cin >> kx >> ky;
for(int i = 1; i <= n; i++) X[i] = AA[i];
for(int i = 1; i <= m; i++) Y[i] = BB[i];
for(int i = 1, p, v; i <= kx; i++) {
cin >> p >> v;
X[p] = v;
}
for(int i = 1, p, v; i <= ky; i++) {
cin >> p >> v;
Y[p] = v;
}
cout << (Sol(n, m, X, Y) || Sol(m, n, Y, X));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!