题目描述
给定一个长度为 n 的数组 a 和一个参数 k。如果一个数组 b 满足以下条件,则称其为“很酷的”:
对于从 k 到 n 的每个下标 i,子数组 [ai−k+1,ai−k+2,…,ai] 都是子数组 [bi−k+1,bi−k+2,…,bi] 的重新排列(即两个长度为 k 的滑动窗口构成的多重集相同)。
现在给定数组 a 和数组 b。数组 a 仅包含 1 到 n 之间的整数;数组 b 包含 1 到 n 之间的整数以及 −1。
请判断是否可以通过将 b 中所有的 −1 替换为 1 到 n 之间的任意整数,使得最终的数组 b 满足上述“很酷的”条件。
输入格式
第一行包含一个整数 t(1≤t≤104),表示测试用例的数量。
每个测试用例的第一行包含两个整数 n 和 k(1≤k≤n≤2⋅105)。
第二行包含 n 个整数 a1,a2,…,an(1≤ai≤n)。
第三行包含 n 个整数 b1,b2,…,bn(1≤bi≤n 或 bi=−1)。
保证所有测试用例中 n 的总和不超过 2⋅105。
voidSolve(){ ll N, K; cin >> N >> K; vector<ll> A(N + 1), B(N + 1); for (int i = 1; i <= N; ++i) cin >> A[i]; for (int i = 1; i <= N; ++i) cin >> B[i];
// target: 第一个窗口 a[1..K] 的多重集,b[1..K] 也必须是它的一个排列 map<ll, ll> target; for (int i = 1; i <= K; ++i) target[A[i]]++;
// free_count: 第一个窗口中,值完全自由(可任意选取)的位置数 int free_count = 0;
// 按模 K 将位置分成 K 条链:链 r 包含位置 r, r+K, r+2K, ... // 每条链恰好有一个位置落在第一个窗口(即位置 r 本身) // // 关键推导:窗口从 [i, i+K-1] 滑到 [i+1, i+K] 时, // 需要 -{b_i} + {b_{i+K}} = -{a_i} + {a_{i+K}} // 因此: // - 若 a_i ≠ a_{i+K}:必须 b_i = a_i 且 b_{i+K} = a_{i+K} // - 若 a_i = a_{i+K}:只需 b_i = b_{i+K}(不一定等于 a) // // 对一条链来说: // - 若链中存在相邻位置 a 值不同("固定链"),则整条链的 b 值全部被唯一确定为 a 值 // - 若链中所有 a 值全相同("自由链"),则链上所有 b 值必须相等,但具体取什么值是灵活的
for (int r = 1; r <= K; ++r) { // 检查链 r 是否所有 a 值都相同 bool all_equal = true; for (int p = r; p + K <= N; p += K) { if (A[p] != A[p + K]) { all_equal = false; break; } }
if (!all_equal) { // 固定链:链上每个位置 b[p] 必须等于 a[p] // 检查已给定的 b 值是否与 a 一致 for (int p = r; p <= N; p += K) { if (B[p] != -1 && B[p] != A[p]) { cout << "NO\n"; return; } } // 位置 r(第一个窗口中的位置)的 b 值确定为 a[r],从 target 中扣除 target[A[r]]--; } else { // 自由链:链上所有 b 值必须相等 // 检查所有已给定的 b 值是否互相一致 ll forced = -1; for (int p = r; p <= N; p += K) { if (B[p] != -1) { if (forced == -1) forced = B[p]; // 第一个给定值,记录 elseif (forced != B[p]) { // 与之前的给定值矛盾 cout << "NO\n"; return; } } } if (forced != -1) target[forced]--; // 链的值已确定,从 target 扣除 else free_count++; // 链的值完全自由,待分配 } }