1. 题面梗概
中译中:给定长度为 n 的数组 a,b,对每个位置 i 可选交换 ai,bi。目标是使得最终 a,b 的多重集相等。
1≤n≤106,∑n≤106
2. 逻辑推导
多重集相等意味着每个数值 v 在 a 数组出现的次数必须恰好等于其在 a,b 总频次的一半。
由此推导:所有数值总频次必须为偶数。
2.1 模型转换
我们将 ai,bi 视作连接数值 ai 和 bi 的一条无向边。
- 不交换:边定向为 ai→bi。
- 交换:边定向为 bi→ai。
若要满足频次平分,则每个点的 入度必须等于出度,这不是就是个 欧拉回路 的问题吗?
2.2 结论
在所有点度数均为偶数的情况下,该图必然可以分解为若干个欧拉回路。我们只需遍历回路,根据遍历时的起点是否为原数组 b 中的元素来决定是否记录交换操作。
3. 代码实现
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| #include <bits/stdc++.h> using namespace std; using ll = long long;
const int maxn = 1e6 + 5;
struct Edge { int to, id; };
vector< Edge > adj[maxn]; bool used[maxn]; int cur[maxn]; int cnt[maxn], a[maxn], b[maxn]; int n; vector< int > ans;
void dfs( int u ) { for( int &i = cur[u]; i < adj[u].size( ); ++ i ) { auto [to, id] = adj[u][i ++]; if( used[id] ) continue; used[id] = 1; if( u == b[id] ) ans.push_back( id ); dfs( to ); } }
void solve( ) { cin >> n; for( int i = 1; i <= n; ++ i ) { adj[i].clear( ); cnt[i] = 0; used[i] = 0; cur[i] = 0; }
for( int i = 1; i <= n; ++ i ) { cin >> a[i]; cnt[ a[i] ] ++; } for( int i = 1; i <= n; ++ i ) { cin >> b[i]; cnt[ b[i] ] ++; }
for( int i = 1; i <= n; ++ i ) { if( cnt[i] % 2 != 0 ) { cout << -1 << '\n'; return ; } }
for( int i = 1; i <= n; ++ i ) { adj[ a[i] ].push_back({ b[i], i }); adj[ b[i] ].push_back({ a[i], i }); }
for( int i = 1; i <= n; ++ i ) { if( cur[i] < adj[i].size( ) ) dfs( i ); }
cout << ans.size( ) << '\n'; for( auto i : ans ) cout << i << ' '; cout << '\n'; }
int main( ) { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _t = 1; cin >> _t; while( _t -- ) solve( ); return 0; }
|
4. 复盘