0%

ABC-470-D - Inverse and Swap

题目大意

D - Inverse and Swap

时间限制:2 秒 / 内存限制:1024 MiB

分值:400400

给定一个 (1,,N)(1, \dots, N) 的排列 P=(P1,,PN)P = (P_1, \dots, P_N)

请依次处理 QQ 个询问,询问有以下两种:

  • 1 x y:交换 PxP_xPyP_y 的值。

  • 2:构造满足以下条件的 (1,,N)(1, \dots, N) 的排列 P=(P1,,PN)P' = (P'_1, \dots, P'_N),并将 P1,,PNP_1, \dots, P_N 的值分别替换为 P1,,PNP'_1, \dots, P'_N。(可以证明满足条件的 PP' 唯一存在。)

    • 对每个满足 1iN1 \leq i \leq N 的整数 ii,都有 PPi=iP_{P'_i} = i

请输出处理完所有询问后 P1,,PNP_1, \dots, P_N 的值。

输入从标准输入给出,格式如下:

1
2
3
4
5
N Q
P_1 P_2 ⋯ P_N
query_1

query_Q

其中 queryq\mathrm{query}_q 表示第 qq 个询问,以下面两种格式之一给出:

1
1 x y
1
2

将处理完所有询问后 P1,,PNP_1, \dots, P_N 的值以空格分隔,输出在一行中。

  • 2N5×1052 \leq N \leq 5 \times 10^5

  • 1Q5×1051 \leq Q \leq 5 \times 10^5

  • (P1,,PN)(P_1, \dots, P_N)(1,,N)(1, \dots, N) 的排列

  • 对于类型 11 的询问,1x<yN1 \leq x < y \leq N

  • 输入中的所有值均为整数

输入

1
2
3
4
5
6
7
5 5
2 1 3 5 4
1 2 4
2
1 2 3
1 3 4
2

输出

1
4 5 2 1 3

说明

每个询问处理完成时,P1,,PNP_1, \dots, P_N 的值如下:

  • 处理完第 11 个询问时,P=(2,5,3,1,4)P = (2,5,3,1,4)

  • 处理完第 22 个询问时,P=(4,1,3,5,2)P = (4,1,3,5,2)

  • 处理完第 33 个询问时,P=(4,3,1,5,2)P = (4,3,1,5,2)

  • 处理完第 44 个询问时,P=(4,3,5,1,2)P = (4,3,5,1,2)

  • 处理完第 55 个询问时,P=(4,5,2,1,3)P = (4,5,2,1,3)

输入

1
2
3
4
5
6
7 4
3 7 5 6 4 2 1
2
2
2
2

输出

1
3 7 5 6 4 2 1

说明

本样例中的 44 个询问均为类型 22,处理完成后 PP 与初始时相同。

输入

1
2
3
4
5
6
7
8
9
10
10 8
7 3 2 4 8 5 10 9 1 6
2
1 4 10
1 6 9
2
1 9 10
1 3 10
2
1 4 6

输出

1
3 10 2 8 6 7 1 5 9 4

说明

本样例包含两种询问混合出现的情况,请按照询问给出的顺序依次处理。

思路讲解

image

那么实际上,排列就是位置和值的对应关系啊至于箭头的方向和哪边是位置,哪边是值,这个其实不是特别重要啊,用一个变量 f 维护一下即可啊。

那么这个叫什么交换操作就只是需要修改一下这个对应关系即可啊,这个就非常简单,你可以理解为我们就是一个无向图啊,然后最后再进行定向即可啊。

当然,还是要注意一下值和位置当前的对应关系啊。

存储的时候,S 和 T都可以看成是一个只有一条出边的有向图邻接表啊,那么实际上,我们存储的就是这个无向图啊。因为无向图就是一条边有两个方向的有向图嘛。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int mp[2][500005];

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int n, q;
cin >> n >> q;

for (int i = 1; i <= n; i++) {
cin >> mp[0][i]; // 位置 i —— 值 mp[0][i]
mp[1][mp[0][i]] = i; // 同一条边,从值那一侧再记一遍
}

int f = 0; // 定向标记

while (q--) {
int op;
cin >> op;

if (op == 2) {
f ^= 1; // 换个方向读,O(1)
continue;
}

int x, y;
cin >> x >> y;

int *S = mp[f]; // 当前的"位置侧"
int *T = mp[f ^ 1]; // 对侧

int u = S[x], v = S[y]; // 原来 x—u、y—v 两条边

// 拆掉 x—u、y—v,重连成 x—v、y—u
// 用 tie + make_tuple 一次性赋值:右边先整体求值,
// 天然避开"先改哪个、后改哪个"的顺序陷阱
tie(S[x], S[y], T[u], T[v]) = make_tuple(v, u, y, x);
}

// 最后按 f 定向,输出对应的那一侧
const int *R = mp[f];
for (int i = 1; i <= n; i++)
cout << R[i] << " \n"[i == n];

return 0;
}

AC代码

https://atcoder.jp/contests/abc470/submissions/78275932

心路历程(WA,TLE,MLE……)