题面
解法
思路还是比较精妙的
- 我们不妨假设 a [ i ] a[i] a[i]表示 i i i这一列是否交换两行的数
- 然后对于一个数 x x x,假设它出现的位置分别为第 i i i列和第 j j j列,如果这两个位置在同一行,那么 i , j i,j i,j之间连接一条为 1 1 1的无向边,表示 a [ i ] ≠ a [ j ] a[i]≠a[j] a[i]̸=a[j],否则在 i , j i,j i,j之间连接一条为 0 0 0的无向边,表示 a [ i ] = a [ j ] a[i]=a[j] a[i]=a[j]
- 然后这个图会被分成若干个连通块,对于每一个连通块,假设 a [ i ] = 0 a[i]=0 a[i]=0的有 x x x个, a [ i ] = 1 a[i]=1 a[i]=1的有 y y y个,那么这个连通块对答案的贡献为 m i n ( x , y ) min(x,y) min(x,y),这个应该比较显然
- 时间复杂度: O ( n ) O(n) O(n)
代码
#include <bits/stdc++.h>
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {int next, num, v;} e[N * 4];
int s0, s1, cnt, vis[N];
vector <int> pos[N];
void add(int x, int y, int v) {
e[++cnt] = (Edge) {e[x].next, y, v};
e[x].next = cnt;
}
void dfs(int x, int col) {
if (col == 0) s0++; else s1++; vis[x] = 1;
for (int p = e[x].next; p; p = e[p].next) {
int k = e[p].num, v = e[p].v;
if (vis[k]) continue; dfs(k, col ^ v);
}
}
int main() {
int n, mx = 0; read(n); cnt = n;
for (int i = 1; i <= n; i++) {
int x; read(x); chkmax(mx, x);
pos[x].push_back(i);
}
for (int i = 1; i <= n; i++) {
int x; read(x); chkmax(mx, x);
pos[x].push_back(-i);
}
for (int i = 1; i <= mx; i++) {
if (pos[i].size() < 2) continue;
int x = pos[i][0], y = pos[i][1], tx = abs(x), ty = abs(y);
if (x < 0 && y > 0 || x > 0 && y < 0) add(ty, tx, 0), add(tx, ty, 0);
else add(ty, tx, 1), add(tx, ty, 1);
}
int ans = 0;
for (int i = 1; i <= n; i++)
if (!vis[i]) {
s0 = 0, s1 = 0;
dfs(i, 0); ans += min(s0, s1);
}
cout << ans << "\n";
return 0;
}