loading...

[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\) 相反:

  1. \(i \neq p_i, i \neq q_i\)
    • \(p_i \neq q_i\):RR;
    • \(p_i = q_i\):RR,BB;
  2. \(i \neq p_i, i = q_i\):RR,RB;
  3. \(i = p_i, i\neq q_i\):RR,BR;
  4. \(i=p_i,i=q_i\):All;

连:

  • 限制边:无需限制边,直接以环点 \(c\) 代替所有环上节点做决策。

  • 考虑下标 \(i\),在 \(a, b\) 对应环点分别为 \(x,y\)

    1. 分情况:
      • RR 会在此处产生贡献:\(y \to x,w=1\)
      • RR,BB 会在此处产生贡献:\(y \to x, w=1\)\(x \to y,w=1\)
    2. R_ 会在此处产生贡献:\(s \to x,w=1\)
    3. _R 会在此处产生贡献:\(y \to t, w=1\)
    4. 一定产生贡献:\(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;
}
posted @ 2026-01-03 17:19  goldspade  阅读(1)  评论(0)    收藏  举报