啊啊啊宝宝你是一个可爱的有思维含量的分讨题

· · 题解

做完了 Simple Task,过了一段时间又想写分讨题,就找到了这里。

这个题也太难了(折磨了我好久好久呜呜),所以来写一篇详细的题解。

当然,如果你不是很无聊没题做,且你也不想锻炼你的代码能力,建议远离这道题目。

分析性质

先使用 ATcoder 传统艺能:找性质。

性质

____ 证明:首先先证明,如果有,一定仅有连续的一段是未经操作的。这个很显然。因为我们只能往最左边或者右边放数,如果出现放的两段之间有一段没动过,且放的两段有至少一段不是最左或最右的,那么不是最左或最右的那一段根本就放不了数。 然后证明有一段是未经操作的。首先,一个数如果操作两次,那么操作总是可以用更少的次数等价替代掉,所以一个数最多只会操作一次。 那么只需要证明操作次数 $<3N$,显然如果操作 $3N$ 次,最中间那个数完全没必要动。所以操作次数一定 $<3N$,所以总存在一段。证毕。 ____ 那么,既然 $N$ 那么小,我们完全可以直接枚举保留的区间,然后就转化成了一个 check 的问题。我们需要在 $O(N^2)$ 时间内 check 出保留某段区间时是否有解。 容易发现一个优美的事实:$B$ 中每一个数是否进行了操作,进行了什么操作是唯一确定的。且无论以什么顺序操作,答案也是根据保留的区间唯一确定的。 假设我们枚举的保留区间是 $[l,r]$,那就把整个 $B$ 划成了三部分:$[1,l)$、$[l,r]$、$(r,3N]$。 这三部分的操作自然是:向左移、不动、向右移。那么,我们只要能够确定出操作的顺序,一切就都解决了。 ## 图论建模 关键就在于判断有没有合法的操作顺序了。由于每个数只会被操作一次,左边和右边的相对顺序就是从中间往两边的。可以抽象成一个图,$u$ 向 $v$ 有一条有向边当且仅当 $u$ 在 $v$ 前面操作,那么这个图的拓扑排序就是一组合法解。只要让这个图无环就可以了。 等等……相对顺序?这四个字提示性极强,直接指向另外四个字——图论建模! 接下来我们就来分析还有哪些相对顺序。最好想的就是同色三个数的相对顺序,我们从这里入手。 ## 分类讨论 不妨用 Left、Middle、Right 来代表左移、放中间不动、右移三种操作。来分类讨论每种情况: (下面的图中,指向 L 和 R 的边对应的是从左往右对应数的操作,$123$ 则是操作顺序,M 不操作就不连边,不考虑操作顺序) ### 一、LLL ![](https://cdn.luogu.com.cn/upload/image_hosting/b51d001l.png) 如图,可以发现 $3$ 无论如何都不能被移动到左边,所以这种情况一旦出现就无解。 ### 二、RRR ![](https://cdn.luogu.com.cn/upload/image_hosting/m4y1rcun.png) 对称,所以同理,$3$ 不能被移动到右边,所以也是一旦出现就无解。 ### 三、LLM ![](https://cdn.luogu.com.cn/upload/image_hosting/b51d001l.png) 这样显然是合法的。不存在其他方案。 ### 四、MRR ![](https://cdn.luogu.com.cn/upload/image_hosting/m4y1rcun.png) 这样显然是合法的。不存在其他方案。 ### 五、LMM ![](https://cdn.luogu.com.cn/upload/image_hosting/kpacdel2.png) 如图,方案唯一。 ### 六、MMR ![](https://cdn.luogu.com.cn/upload/image_hosting/h5n29rzc.png) 如图,方案唯一。 ### 七、MMM ![](https://cdn.luogu.com.cn/upload/image_hosting/sfoqtc6h.png) 最简单的情况,根本不用动。 ### 八、LLR 会发现有两种情况。 第一种: ![](https://cdn.luogu.com.cn/upload/image_hosting/vvhpuhfn.png) 第二种: ![](https://cdn.luogu.com.cn/upload/image_hosting/5mii5s00.png) 但是无论哪一种,都有 R 在最后一个 L 前面,不妨只加这一个约束。 ### 九、LRR 同理,也是两种情况。 第一种: ![](https://cdn.luogu.com.cn/upload/image_hosting/d3c4rca4.png) 第二种: ![](https://cdn.luogu.com.cn/upload/image_hosting/e8duhjnu.png) 依然同理,无论哪一种,都有 L 在最后一个 R 前面,不妨只加这一个约束。 ### 十、LMR 这是本题的核心,也是最大的难点。 第一种: ![](https://cdn.luogu.com.cn/upload/image_hosting/94yzww9d.png) 第二种: ![](https://cdn.luogu.com.cn/upload/image_hosting/5nxh2eqh.png) 这种情况就没什么性质了,所以我们需要通过 2-SAT 来判断是否存在一种合法的顺序。 ## 2-SAT 图构造 我们需要用一种常见的方式——边转点。 我们知道,前面我们确定操作顺序的方式是位置之间连边。现在我们要确定第十种情况是先右后左还是先左后右。 我们不妨建一个新图用 $[1,N]$ 号点表示第 $i$($1 \leq i \leq N$)种颜色是否是先右后左(不妨把原图左边的点往上放,称为向上连边),用 $[N + 1, 2N]$ 号点表示第 $i - N$($N + 1 \leq i \leq 2N$)种颜色是否是向下连。 详细讲一下新图的结构:$[2, l - 1]$ 在上面,每一个点向左连边;$[r + 1, 3N - 1]$ 在下面,每一个点向右连边;上下有一些特殊边。 接下来,只要把边连出来就可以了! ## 合法判断(难点) 首先前面我们讲过了,出现 LLL 或者 RRR 一定无解。 然后来分析还有什么情况是无解的。 ### LLR 和 LRR 由于新图只涉及 LMR,在旧图上考虑。 可以发现,LLR 一定是向上连,LRR 一定是向下连。那么如果 LLR 在 LRR 右边,就会有下、右、上、左形成环。这种情况一定是无解的。 类似这样(不用管编号): ![](https://cdn.luogu.com.cn/upload/image_hosting/3pxjvuea.png) ### LMR 和 LLR/LRR 显然,如果 LMR 在 LLR 左边或者 LRR 右边,就一定要和 LLR 或 LRR 同向,否则就会形成环。图和上面其实是一样的,只不过 LMR 取代了其中一条边,我们要为 LMR 定向。那么这个时候在新图上让不可能的方向向可能的方向连一条边,也就是说当取到了不可能的方向就一定无解。 ### LMR 和 LMR 那显然,两条边要同向,左边的边向下了,右边的边就不能向上;右边的边向上了,左边的边就不能向下。在新图上连边即可。 ____ 上面这几部分复杂度为 $O(N^2)$。 ### MMM、LMM、MMR、LLM、MMR 这五种情况要判断是否有一个 M 是合法的。我们对于 $l \leq i \leq r$ 的 $B_i$ 进行讨论,维护一个指针从左往右扫 $A$。需要基础的贪心。 显然指针不会往左,因为不移动的情况下相对顺序是不改变的,所以这部分复杂度为 $O(N)$。 #### MMM 找到和当前 $B_i$ 同色的配对就可以了。尽可能左边一定不劣。 #### LMM 用 $1,2,3$ 表示三个相同的数从左到右的顺序。那么 $B$ 中的 $1,2,3$ 对应 $A$ 中的 $2,1,3$。 所以只有 $A$ 中最左/右两个位置的可以配掉。 #### MMR 是对称的。$B$ 中的 $1,2,3$ 对应 $A$ 中的 $1,3,2$。 所以依旧只有 $A$ 中最左/右两个位置的可以配掉。 #### LLM 可以发现这种情况相对顺序不改变,非常好判。 #### MRR 对称,相对顺序也是不改变的。 至此,这几种情况也解决掉了,下面还有 LMR 的两个判断,是本题最难也是最烦的部分。 ### LMR 的 M 与其他 M 之间的限制 其他的 M 都已经被处理掉了,假设左边的 M 在 $A$ 中位置是 $p_l$,右边的 M 是 $p_r$,那么当前 M 的位置 $p_i$ 一定有 $p_l<p_i<p_r$。 首先有如果 $R < p_l$ 或者 $L > p_r$,显然是无解的。 来分类讨论其他几种情况: #### $R>p_r

分四种情况讨论。

p_r 在右半部分:

向下连边显然错位了:

向上连呢?

是可以的!

那如果 p_r 在左半部分呢?

分析向上连:

依旧是可以的。

向下连:

错位了。

L<p_l

是对称的,用基本相同的方法分四种情况讨论就可以了,懒得画图了呜呜。

其余情况

会发现对建边没有限制。

LMR 的 M 与 LMR 的 M 之间的限制

显然两个 M 位置顺序不能变,没有其他要求。

假设有 l \leq i<j \leq rB_i \neq B_j。设 L_i,R_i,L_j,R_jB_iB_jA 中对应的左右两个位置。

那么(有了前面的位置对应关系,这一部分相信可以自行画图,我画图比较麻烦,因为有 12 种情况,草稿纸上的图没法拍照,所以只给结论):

R_j<L_i

必定是越位的。无解。

L_j<L_i

#### $R_j<R_i #### $L_j<R_i ____ 恭喜你建完了所有的边,现在,跑一个 2-SAT 就做完了。 ::::info[代码] ```cpp #include<bits/stdc++.h> using namespace std; int n, N, a[105], b[105], status[105], L[105], R[105], aL[105], aR[105]; int tim, scnt, dfn[205], low[205], col[205], ins[205], pos[205], ans; vector<int> LLL, RRR, LLR, LRR, LLM, MRR, LMM, MMR, MMM, LMR;//十种情况 vector<int> G[205], vec[205]; stack<int> S; void genans(int now){ if (ans == -1) ans = now; else ans = min(ans, now); } void add(int u, int v){ G[u].emplace_back(v); } void clr(){//清空 LLL.clear(); RRR.clear(); LLR.clear(); LRR.clear(); LLM.clear(); MRR.clear(); LMM.clear(); MMR.clear(); MMM.clear(); LMR.clear(); tim = 0; memset(L, 0, sizeof L); memset(R, 0, sizeof R); memset(aL, 0, sizeof L); memset(aR, 0, sizeof R); memset(status, 0, sizeof status); memset(pos, 0, sizeof pos); memset(dfn, 0, sizeof dfn); memset(low, 0, sizeof low); memset(col, 0, sizeof col); memset(ins, 0, sizeof ins); while (!S.empty()) S.pop(); for (int i = 1; i <= 200; i++){ G[i].clear(); vec[i].clear(); } } void Tarjan(int u){ dfn[u] = low[u] = ++tim; S.push(u); ins[u] = 1; for (auto v : G[u]){ if (!dfn[v]){//树边 Tarjan(v); low[u] = min(low[u], low[v]); }else if (ins[v]) low[u] = min(low[u], dfn[v]); } if (dfn[u] == low[u]){ scnt++; while (1){ int v = S.top(); S.pop(); col[v] = u; ins[v] = 0; if (u == v) break; } } } void genstatus(int l, int r){ for (int i = 1; i <= N; i++){ if (i < l) status[i] = 1; else if (l <= i && i <= r) status[i] = 2; else if (i > r) status[i] = 3; } for (int i = 1; i <= N; i++){//处理出每种颜色的位置 vec[b[i]].emplace_back(i); if (status[i] == 1){ if (!L[b[i]]) L[b[i]] = i; }else if (status[i] == 3) R[b[i]] = i; if (!aL[a[i]]) aL[a[i]] = i; aR[a[i]] = i; } } int GetType(int i){//获取一个颜色的类型 int cntL = 0, cntM = 0, cntR = 0; for (int pos : vec[i]){ cntL += (status[pos] == 1); cntM += (status[pos] == 2); cntR += (status[pos] == 3); } if (cntL == 3)//按照十种情况分类 return 1;//LLL else if (cntR == 3) return 2;//RRR else if (cntL == 2 && cntR == 1) return 3;//LLR else if (cntL == 1 && cntR == 2) return 4;//LRR else if (cntL == 2 && cntM == 1) return 5;//LLM else if (cntM == 1 && cntR == 2) return 6;//MRR else if (cntL == 1 && cntM == 2) return 7;//LMM else if (cntM == 2 && cntR == 1) return 8;//MMR else if (cntM == 3) return 9;//MMM else if (cntL == 1 && cntR == 1 && cntM == 1) return 10;//LMR } void RubbishSorting(int l, int r){ for (int i = 1; i <= n; i++){ int cntL = 0, cntM = 0, cntR = 0; for (int pos : vec[i]){ cntL += (status[pos] == 1); cntM += (status[pos] == 2); cntR += (status[pos] == 3); } if (cntL == 3)//按照十种情况分类 LLL.emplace_back(i); else if (cntR == 3) RRR.emplace_back(i); else if (cntL == 2 && cntR == 1) LLR.emplace_back(i); else if (cntL == 1 && cntR == 2) LRR.emplace_back(i); else if (cntL == 2 && cntM == 1) LLM.emplace_back(i); else if (cntM == 1 && cntR == 2) MRR.emplace_back(i); else if (cntL == 1 && cntM == 2) LMM.emplace_back(i); else if (cntM == 2 && cntR == 1) MMR.emplace_back(i); else if (cntM == 3) MMM.emplace_back(i); else if (cntL == 1 && cntR == 1 && cntM == 1) LMR.emplace_back(i); } } int check(int l, int r){ //钦定 L 上 R 下,向上连为 [1, n],向下连为 [n + 1, 2n] genstatus(l, r); RubbishSorting(l, r); //判断 LLL 和 RRR 的无解情况 if (LLL.size() || RRR.size()) return -1; //LLL 最右侧的一个无法移到左边;RRR 对称,所以存在即无解 //判断 LRR 和 LLR 的无解情况 for (auto c1 : LRR) for (auto c2 : LLR){ if (L[c1] < L[c2] && R[c1] < R[c2]) return -1; } //样例很良心给了这个,这样会成环 //判断 LMR 和 LLR/LRR 的情况 for (auto c1 : LMR){ for (auto c2 : LLR){ if (L[c1] < L[c2] && R[c1] < R[c2]){ //必须向上连 add(c1 + n, c1); } } for (auto c2 : LRR){ if (L[c2] < L[c1] && R[c2] < R[c1]){ //必须向下连 add(c1, c1 + n); } } } //通过 LLR 和 LRR 可以唯一确定 LMR 的方向 //判断 LMR 和 LMR 的情况 for (auto c1 : LMR){ for (auto c2 : LMR){ if (L[c1] < L[c2] && R[c1] < R[c2]) add(c1 + n, c2 + n); //左边向下,右边不能向上,否则成环 if (L[c2] < L[c1] && R[c2] < R[c1]) add(c1, c2); //右边向上,左边不能向下,否则成环 } } //两个 LMR 需要同向 //判断 MMM、LLM、MRR、LMM、MMR 是否合法 int p = 1; for (int i = l; i <= r; i++){//中间保留部分 int typ = GetType(b[i]); if (typ == 9){//MMM while (p <= N && a[p] != b[i]) p++; if (p > N) return -1; pos[i] = p; p++; }else if (typ == 7){//LMM while (p <= N && p != aL[b[i]] && p != aR[b[i]]) p++; if (p > N) return -1; pos[i] = p; p++; }else if (typ == 8){//MMR while (p <= N && p != aL[b[i]] && p != aR[b[i]]) p++; if (p > N) return -1; pos[i] = p; p++; }else if (typ == 5){//LLM while (p <= N && p != aR[b[i]]) p++; if (p > N) return -1; pos[i] = p; p++; }else if (typ == 6){//MRR while (p <= N && p != aL[b[i]]) p++; if (p > N) return -1; pos[i] = p; p++; } } //处理出每个 M 在 a 中对应的位置 // 判断 LMR 的 M 限制 for (int i = l; i <= r; i++){ int x = GetType(b[i]); if (x != 10) continue; //找到左边和右边被限制的位置,显然当前位置一定得在两个位置中间 int ml = 0, mr = N + 1; for (int j = i - 1; j >= l; j--){ if (pos[j]){ ml = pos[j]; break; } } for (int j = i + 1; j <= r; j++) if (pos[j]){ mr = pos[j]; break; } //当前位置应该在 (ml, mr) 区间内 if (aL[b[i]] > mr || aR[b[i]] < ml) return -1; if (aL[b[i]] < ml){ //向下连 add(b[i], b[i] + n); } if (aR[b[i]] > mr){ //向上连 add(b[i] + n, b[i]); } } //基于 M 的位置限制 // 显然有 LMR 和 LMR 之间的 M 不能相互越位,进行判断 for (int i = l; i <= r; i++){ int x = GetType(b[i]); if (x != 10) continue; for (int j = i + 1; j <= r; j++){ int y = GetType(b[i]); if (y != 10) continue; if (aR[b[j]] < aL[b[i]]) return -1; if (aL[b[j]] < aL[b[i]]){ add(b[i], b[j] + n); add(b[j], b[i] + n); } if (aR[b[j]] < aR[b[i]]){ add(b[i] + n, b[j]); add(b[j] + n, b[i]); } if (aL[b[j]] < aR[b[i]]){ add(b[i] + n, b[j] + n); add(b[j], b[i]); } } } //一定要搞清楚了 //Tarjan 主体 for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i); // 判断是否有解 for (int i = 1; i <= n; i++) if (col[i] == col[i + n]) return -1;//矛盾了 return N - (r - l + 1); } int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin >> n; N = 3 * n; ans = -1; bool qwq = 0; for (int i = 1; i <= N; i++) cin >> a[i]; for (int i = 1; i <= N; i++){ cin >> b[i]; if (a[i] != b[i]) qwq = 1; } if (!qwq){ cout << 0; exit(0); } for (int len = N - 1; len; len--){ for (int l = 1; l + len - 1 <= N; l++){ clr(); int r = l + len - 1; genans(check(l, r)); if (ans != -1){ cout << ans; exit(0); } } } cout << ans; return 0; } ``` :::: ## 后记 是微渺/的希望/我们依然前行没有光指引/往前吧失去吧不要停留。 这题确实很可怕,但是,把时间用在所热爱的一切上,用尽全力,总会看到成果的。