1 条题解
-
0
问题重述
我们有一个初始数组 ,经过 次操作后得到数组 。第 次操作是:
已知最终数组 和所有操作 ,要求构造一个可能的初始数组 ,或判断无解。
核心思想:逆向推导约束
我们不知道初始数组 ,但知道最终数组 。如果我们从最终状态反向推导,可以得到每个位置 在初始时至少应该是多少。
定义 为位置 在某个时刻必须满足的下界。初始时,最终状态要求 ,所以 。
反向操作规则
考虑最后一次操作(第 次):
操作后,我们有 ,且 ,。
现在我们想知道操作前这些位置的下界。
分析:
- 对于 ,这些位置在操作中没有被修改,所以它们的下界不变:。
- 对于 :操作前 的值会被覆盖,所以操作前它没有任何限制(可以被任意值覆盖)。因此 。
- 对于 :操作后 。
由于 ,我们有 。
这意味着 且 。
另外,操作后 保持不变,所以 。
同理 。
因此,反向更新的规则是:
$$l'_{x} = \max(l_x, l_z), \quad l'_{y} = \max(l_y, l_z) $$其余位置不变。
算法步骤
- 初始化:设 。
- 逆向遍历操作 到 :
- 记当前 为 。
- 将 设为 (因为被覆盖)。
- 更新
- 更新
- 得到 数组后,令 。
- 正向验证:用 作为初始数组,按原操作顺序执行一遍,检查最终结果是否等于 。
- 如果相等,输出 。
- 如果不相等,输出 。
为什么这样构造是正确的?
- 必要性:任何可行的初始数组 必须满足 ,否则无法达到 。
- 充分性:取 是满足条件的最小可能值。如果 经过正向操作后得到的结果不等于 ,说明矛盾,无解。否则, 就是一个解。
时间复杂度
- 逆向更新:
- 正向模拟:
- 总复杂度: 每个测试用例。
代码实现
// 逆向求出下界 l(存储在 c 数组中) for(int i = q ; i >= 1 ; i --){ int v = c[z[i]]; // 当前 l[z_i] 的值 c[z[i]] = 0; // 被覆盖,下界变为 0 c[x[i]] = max(c[x[i]], v); // 更新 x_i 的下界 c[y[i]] = max(c[y[i]], v); // 更新 y_i 的下界 } // a = l for(int i = 1 ; i <= n ; i ++) a[i] = c[i]; // 正向模拟验证 for(int i = 1 ; i <= q ; i ++) c[z[i]] = min(c[x[i]], c[y[i]]); // 检查是否与 b 一致 for(int i = 1 ; i <= n ; i ++) if(b[i] != c[i]) { cout << "-1\n"; return; }
示例验证
样例1:
n=2, q=1, b=[1,2] 操作: (2,1,2)逆向:
- 初始
- : ,, 得 正向:
- ,最终 ,输出
-1。
样例2:
n=3, q=2, b=[1,2,3] 操作1: (2,3,2) 操作2: (1,2,1)逆向:
- 初始
- : ,,,
- : ,,,
得
正向模拟不改变数组,匹配 ,输出
1 2 3。
总结
本题的关键是逆向推导下界,然后正向验证。这种方法利用了 操作的性质,通过最大值传递约束,最终构造出最小可行解。如果最小解都不满足,则无解。
- 1
信息
- ID
- 7131
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者