[AGC038F]Two Permutations
aeemnSttt
给定 \(p,q\) 两个排列,构造两个排列 \(a,b\),满足 \(\forall i\):
- \(a_i=i \lor a_i=p_i\)
- \(b_i=i \lor b_i=q_i\)
求序列 \(\sum [a_i \neq b_i]\) 的最大值。
\(n \le 10^5\)。
ilnooStu
将原问题以二分图形式描述限制(是排列),以 \(a\) 为例;
拆点 \(x,x'\),\(x\) 代指下标,\(x'\) 代指值;
连接无向边:\((i,i'),(i,(p_i)')\),选择一个匹配就等价于 \(a_i=i\) 或 \(p_i\),隐含不能将一个值同时分配给多个下标。
总度数显然为 \(2n=\) 总点数,形成若干个偶环。每个偶环对应 \(2\) 种决策:使环内所有下标点 \(i\) 对应 \(a_i=i\) 或 \(a_i=p_i\)。为环编号 \(c\),设环内节点集为 \(S_c\)。
以上为限制条件。
转化为求 \(a_i=b_i\) 最少。
| \(a\)\\(b\) | R | B |
|---|---|---|
| R | \(i,i\) | \(i,q_i\) |
| B | \(p_i,i\) | \(p_i,q_i\) |
最小割建模,令 \(a\) 在源点集合为 R,在汇点集合为 B,\(b\) 相反:
- \(i \neq p_i, i \neq q_i\):
- \(p_i \neq q_i\):RR;
- \(p_i = q_i\):RR,BB;
- \(i \neq p_i, i = q_i\):RR,RB;
- \(i = p_i, i\neq q_i\):RR,BR;
- \(i=p_i,i=q_i\):All;
连:
-
限制边:无需限制边,直接以环点 \(c\) 代替所有环上节点做决策。
-
考虑下标 \(i\),在 \(a, b\) 对应环点分别为 \(x,y\):
- 分情况:
- RR 会在此处产生贡献:\(y \to x,w=1\);
- RR,BB 会在此处产生贡献:\(y \to x, w=1\),\(x \to y,w=1\);
- R_ 会在此处产生贡献:\(s \to x,w=1\);
- _R 会在此处产生贡献:\(y \to t, w=1\);
- 一定产生贡献:\(s\to t,w=1\);
- 分情况:
复杂度 \(\mathcal O(n \sqrt n)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = (l); i <= (r); ++i)
#define per(i, r, l) for (int i = (r); i >= (l); --i)
#define U(i, l, r) for (int i = (l); i < (r); ++i)
#define int long long
#define LL long long
#define popc __builtin_popcount
#define FASTIO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
const int N = 2e5+5, M = N<<1, mod = 1e6+3, inf = 2e9;
const double eps = 1e-9;
int n, m;
struct Flow {
int head[N], ver[M], ne[M], ed[M], tot, s, t;
Flow(int s, int t) : s(s), t(t) { tot = 1, memset(head, 0, sizeof(head)); }
inline void clear() { tot = 1, memset(head, 0, sizeof(head)); }
inline void adde(int u, int v, int w) {
ver[++tot] = v, ed[tot] = w, ne[tot] = head[u], head[u] = tot;
}
inline void add(int u, int v, int w) { adde(u, v, w), adde(v, u, 0); }
int dep[N], cur[N];
inline bool bfs() {
queue<int> q;
memset(dep, 0, sizeof(dep));
dep[s] = 1, cur[s] = head[s];
q.push(s);
while (q.size()) {
int x = q.front(); q.pop();
for (int i = head[x]; i; i = ne[i]) {
int y = ver[i];
if (!ed[i] || dep[y]) continue;
dep[y] = dep[x]+1, cur[y] = head[y];
q.push(y);
}
}
return dep[t];
}
int dinic(int x, int flow) {
int res = 0;
if (x==t) return flow;
for (int i = cur[x]; i&&flow; i = ne[i]) {
int y = ver[i]; cur[x] = i;
if (!ed[i] || dep[y] != dep[x]+1) continue;
int r = dinic(y, min(flow, ed[i]));
if (r == 0) dep[y] = INT_MAX;
ed[i] -= r, flow -= r, ed[i^1] += r, res += r;
}
return res;
}
int maxflow() {
int pf = 0;
while (bfs())
pf += dinic(s, LONG_LONG_MAX);
return pf;
}
} o_o(0, 200001);
int fa[N], c[N], d[N];
int find(int x) {
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
inline void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return;
if (y < x) swap(x, y);
fa[y] = x;
}
int p[N], q[N];
inline void Q__Q() {
cin >> n;
rep(i, 1, n) cin >> p[i], ++p[i];
rep(i, 1, n) cin >> q[i], ++q[i];
iota(fa, fa+n+1, 0);
rep(i, 1, n) merge(i, p[i]);
int cnt = 0;
rep(i, 1, n) {
if (fa[i] == i) c[i] = ++cnt;
else c[i] = c[find(i)];
}
iota(fa, fa+n+1, 0);
rep(i, 1, n) merge(i, q[i]);
rep(i, 1, n) {
if (fa[i] == i) d[i] = ++cnt;
else d[i] = d[find(i)];
}
rep(i, 1, n) {
if (p[i] != i && q[i] != i) {
o_o.add(d[i], c[i], 1);
if (p[i]==q[i]) o_o.add(c[i], d[i], 1);
} else if (p[i] != i) o_o.add(0, c[i], 1);
else if (q[i] != i) o_o.add(d[i], o_o.t, 1);
else o_o.add(0, o_o.t, 1);
}
cout << n-o_o.maxflow() <<'\n';
}
signed main() {
#ifdef LOCAL
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
FASTIO;
int _=1;
// cin >> _;
while (_--) Q__Q();
return 0;
}

浙公网安备 33010602011771号