给定一个排列p,定义一种操作方式是,选择三个下标a,b,c,然后让p[c]=p[b],p[b]=p[a],p[a]=p[c](循环右移一位),下标a,b,c的大小关系任意,但必须两两不同.要求用k次操作让p有序,输出一种合法解(不要求最小化),无解输出-1$n<=2e5, k<=\lfloor \frac{n}{2} \rfloor$
做法
先考虑一下n=3的时候,可以发现该操作会形成两个等价类,{(1, 2, 3), (3, 1, 2), (2, 3, 1)}、 {(3, 2, 1), (1, 3, 2), (2, 1, 3)},因此第二个集合中的排列就不可能排成有序. 然而这好像没什么用.从k的大小入手,k<=n/2,说明每次操作至少要让两个元素归位,每次只要操作(i, p[i], p[p[i]])即可,但是有可能i==p[p[i]],即为一个二元环.下面考虑{2, 1, 4, 3}这个排列,它由两个二元环组成,可以发现只要操作(2,3,4),(1,2,3)即可,就是说两个二元环可以通过两次操作排成有序.所以我们只要先对长度大于2的环进行操作,然后把二元环存起来.最后如果二元环有偶数个,两两解决,否则无解.如果二元环有奇数个,则逆序对数有奇数个,而可以发现该操作每次只能变化偶数个逆序对,其实就对应了上面的两个等价类.
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
| #include <bits/stdc++.h> #define mp make_pair #define pb push_back #define sz(x) (int)x.size() #define all(x) begin(x), end(x) #define fi first #define se second #define debug(x) cerr << #x << " " << x << '\n' using namespace std; using ll = long long; using pii = pair<int,int>; using pli = pair<ll,int>; const int INF = 0x3f3f3f3f, N = 2e5 + 5; const ll LINF = 1e18 + 5; constexpr int mod = 1e9 + 7; int n, k, a[N], p[N], b[N]; bool vis[N]; void upd(int x, int y, int z) { assert(x!=y); assert(y!=z); assert(x!=z); int tmp = a[z]; a[z] = a[y]; p[a[y]] = z; a[y] = a[x]; p[a[x]] = y; a[x] = tmp; p[tmp] = x; } void solve() { scanf("%d%d", &n, &k); for(int i=1; i<=n; i++) { scanf("%d", a+i); p[a[i]] = i; } vector <array<int,3>> op; for(int i=1; i<=n; i++) { vis[i] = 0; if(i!=p[i]) { int x = p[i], y = i, z = a[i]; if(z==x) continue; op.pb({y, z, x}); upd(y, z, x); } } vector <pii> loop; for(int i=1; i<=n; i++) { if(i!=p[i] && p[i]==a[i] && !vis[p[i]]) { loop.pb({i, p[i]}); vis[i] = vis[p[i]] = 1; } } if(sz(loop)&1) { puts("-1"); return; } for(int i=0; i<sz(loop); i+=2) { int a = loop[i].fi, b = loop[i].se, c = loop[i+1].fi, d = loop[i+1].se; op.pb({b, c, d}); upd(b, c, d); op.pb({a, b, c}); upd(a, b, c); } if(sz(op)>k || !is_sorted(a+1, a+n+1)) puts("-1"); else { cout << sz(op) << '\n'; for(auto it : op) printf("%d %d %d\n", it[0], it[1], it[2]); } } int main() { int T; scanf("%d", &T); while(T--) solve(); return 0; }
|