题意
给你两个排列,分别用他们做一个新排列,求两个新排列的最多不同位置个数。
用排列a生成新排列要满足:一个位置i要么是i,要么是a[i].
思路
- 首先观察变换,发现要么是一个轮换不变,要么是轮换位移一下。
- 即可以选择每个轮换是否换。
- 发现这个之后,我尝试了构通解的图,发现并构不出来…
- 只能暴力考虑每种情况:考虑每个位置的贡献:
- 因为每个位置最多有两种取值,因此情况数是比较少的。
- ,此位置必定有1的代价
- ,若两边都换或都不换,则有1的代价
- ,这两种是某一边不换则有1的代价。
- ,都不换则有1的代价
构图非常套路,这种状态相同的有额外代价的,只需要把两边“反过来”。
- P在S集是不选,反之是选。
- Q在S集是选,反之是不选。
在对应的轮换上连边即可,这样是二分图。(如果不将一个轮换中的点合并,就不是二分图,跑的很慢。。。)
跑最小割即可,用dinic。(一定要加当前弧,不然复杂度有问题)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, inf = 1e9;
int n, P[N], Q[N];
int S, T, final[N], nex[N * 8], to[N * 8], tot = 1, f[N * 8];
void _link(int x, int y, int w) {
to[++tot] = y, nex[tot] = final[x], final[x] = tot;
f[tot] = w;
}
void link(int x, int y, int w) {
_link(x, y, w), _link(y, x, 0);
}
int vis[N], ans;
int dis[N], cur[N];
void dinic() {
static queue<int> Q;
memset(cur, 0, sizeof cur);
memset(dis, 127, sizeof dis);
dis[S] = 0; Q.push(S);
while (Q.size()) {
int x = Q.front(); Q.pop();
for(int i = final[x]; i; i = nex[i]) if(f[i]){
int y = to[i];
if(dis[x] + 1 < dis[y]) {
dis[y] = dis[x] + 1;
Q.push(y);
}
}
}
}
int go(int x, int w) {
if (x == T) return w;
int used = 0;
if (cur[x] == 0) cur[x] = final[x];
for(int i = cur[x]; i; i = nex[i]) {
if (f[i]) {
int y = to[i];
if (dis[x] + 1 == dis[y]) {
int take = go(y, min(w - used, f[i]));
f[i] -= take;
f[i ^ 1] += take;
used += take;
if (used == w) return w;
}
}
cur[x] = i;
}
return used;
}
int lab[N], lab2[N];
int labcnt;
int main() {
int g = clock();
freopen("f.in", "r", stdin);
cin >> n;
for(int i = 1; i <= n; i++) scanf("%d", &P[i]), P[i]++;
for(int i = 1; i <= n; i++) scanf("%d", &Q[i]), Q[i]++;
S = n + n + 1, T = S + 1;
for(int i = 1; i <= n; i++) if(!vis[i]) {
labcnt++;
int t = i;
vis[t] = 1;
lab[t] = labcnt;
while (vis[P[t]] == 0) {
// _link(P[t], t, inf);
// _link(t, P[t], inf);
t = P[t];
vis[t] = 1;
lab[t] = labcnt;
}
}
memset(vis, 0, sizeof vis);
for(int i = 1; i <= n; i++) if(!vis[i]) {
labcnt++;
int t = i; vis[t] = 1;
lab2[t] = labcnt;
while (vis[Q[t]] == 0) {
// _link(Q[t] + n, t + n, inf);
// _link(t + n, Q[t] + n, inf);
t = Q[t];
vis[t] = 1;
lab2[t] = labcnt;
}
}
ans = n;
for(int i = 1; i <= n; i++) {
if (P[i] == i && Q[i] == i) {
ans --;
} else {
if (P[i] == i && Q[i] != i) { //n+i默认Q[i](在S集合),否则就有1的代价
link(S, lab2[i], 1);
} else
if (P[i] != i && Q[i] == i) { //i默认P[i](在T集合)
link(lab[i], T, 1);
} else
if (P[i] != i && Q[i] != i && P[i] != Q[i]) { //同时选i有1的代价
link(lab[i], lab2[i], 1);
} else { // P[i] == Q[i] 选法相同有1的代价
_link(lab[i], lab2[i], 1);
_link(lab2[i], lab[i], 1);
}
}
}
cerr << labcnt << endl;
while (dinic(), dis[T] <= inf) {
ans -= go(S, inf);
cerr << ans << endl;
}
cout << ans << endl;
cerr << clock() - g << endl;
}