P4677DeerInZooDivOne

xiaruize發表於2024-04-08

費用流 #二分圖最大權匹配 #dp

\(dp_{x,y}\) 表示以 \(x,y\) 為對應點的最大同構子樹的大小

對於一對點,轉移為將 \(x,y\) 中的點按照一定順序對應

那麼問題轉化為如何求一組匹配,使得兩兩匹配的權值儘可能大,即一個二分圖最大權匹配,可以費用流解決

然後列舉斷開的每條邊,對左右都做上述的 \(dp\) ,然後選擇最大的貢獻答案

// Author: xiaruize
const int INF = 0x3f3f3f3f3f3f3f3f;
const int MOD = 1000000007;
const int N = 50 + 10;

namespace MCMF
{
    const int MAXN = 105, MAXM = 100005, INF = 0x7fffffff;
    int head[MAXN], cnt = 1;
    struct Edge
    {
        int to, w, c, next;
    } edges[MAXM * 2];
    inline void add(int from, int to, int w, int c)
    {
        edges[++cnt] = {to, w, c, head[from]};
        head[from] = cnt;
    }
    inline void addEdge(int from, int to, int w, int c)
    {
        add(from, to, w, c);
        add(to, from, 0, -c);
    }
    int s, t, dis[MAXN], cur[MAXN];
    bool inq[MAXN], vis[MAXN];
    queue<int> Q;
    bool SPFA()
    {
        while (!Q.empty())
            Q.pop();
        copy(head, head + MAXN, cur);
        fill(dis, dis + MAXN, INF);
        dis[s] = 0;
        Q.push(s);
        while (!Q.empty())
        {
            int p = Q.front();
            Q.pop();
            inq[p] = 0;
            for (int e = head[p]; e != 0; e = edges[e].next)
            {
                int to = edges[e].to, vol = edges[e].w;
                if (vol > 0 && dis[to] > dis[p] + edges[e].c)
                {
                    dis[to] = dis[p] + edges[e].c;
                    if (!inq[to])
                    {
                        Q.push(to);
                        inq[to] = 1;
                    }
                }
            }
        }
        return dis[t] != INF;
    }
    int dfs(int p = s, int flow = INF)
    {
        if (p == t)
            return flow;
        vis[p] = 1;
        int rmn = flow;
        for (int eg = cur[p]; eg && rmn; eg = edges[eg].next)
        {
            cur[p] = eg;
            int to = edges[eg].to, vol = edges[eg].w;
            if (vol > 0 && !vis[to] && dis[to] == dis[p] + edges[eg].c)
            {
                int c = dfs(to, min(vol, rmn));
                rmn -= c;
                edges[eg].w -= c;
                edges[eg ^ 1].w += c;
            }
        }
        vis[p] = 0;
        return flow - rmn;
    }
    int maxflow, mincost;
    inline void run(int s, int t)
    {
        MCMF::s = s, MCMF::t = t;
        while (SPFA())
        {
            int flow = dfs();
            maxflow += flow;
            mincost += dis[t] * flow;
        }
    }

    void init()
    {
        maxflow = mincost = 0;
        mms(head, 0);
        // mms(edges, 0);
        cnt = 1;
    }

} // namespace MCMF

int n;
pii eg[N];
vector<int> g[N], vec[N];
int dp[N][N];
bool vis[N];

void tagvis(int x)
{
    vis[x] = true;
    for (auto v : vec[x])
    {
        if (!vis[v])
            tagvis(v);
    }
}

void buildtree(int x, int fa)
{
    g[x].clear();
    for (auto v : vec[x])
    {
        if (v == fa)
            continue;
        g[x].push_back(v);
        buildtree(v, x);
    }
}

void calc(int x, int y)
{
    if (dp[x][y] != -1)
        return;
    for (auto u : g[x])
    {
        for (auto v : g[y])
            calc(u, v);
    }
    MCMF::init();
    for (auto u : g[x])
    {
        MCMF::addEdge(n + 1, u, 1, 0);
    }
    for (auto v : g[y])
        MCMF::addEdge(v, n + 2, 1, 0);
    for (auto u : g[x])
    {
        for (auto v : g[y])
        {
            MCMF::addEdge(u, v, 1, -dp[u][v]);
        }
    }
    MCMF::run(n + 1, n + 2);
    dp[x][y] = (-MCMF::mincost) + 1;
}

void solve()
{
    cin >> n;
    rep(i, 1, n)
    {
        cin >> eg[i].first;
        eg[i].first++;
    }
    cin >> n;
    rep(i, 1, n)
    {
        cin >> eg[i].second;
        eg[i].second++;
    }
    n++;
    int res = 0;
    rep(i, 1, n - 1)
    {
        rep(j, 1, n)
        {
            vec[j].clear();
            g[j].clear();
            vis[j] = false;
        }
        rep(j, 1, n - 1)
        {
            if (i == j)
                continue;
            vec[eg[j].first].push_back(eg[j].second);
            vec[eg[j].second].push_back(eg[j].first);
        }
        tagvis(1);
        buildtree(1, 0);
        rep(j, 1, n)
        {
            if (vis[j])
                continue;
            mms(dp, -1);
            buildtree(j, 0);
            rep(k, 1, n)
            {
                if (vis[k])
                {
                    calc(k, j);
                    res = max(res, dp[k][j]);
                }
            }
        }
    }
    cout << res << endl;
}

#ifndef ONLINE_JUDGE
bool end_of_memory_use;
#endif

signed main()
{
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int testcase = 1;
    // cin >> testcase;
    while (testcase--)
        solve();
#ifndef ONLINE_JUDGE
    cerr << "Memory use:" << (&end_of_memory_use - &start_of_memory_use) / 1024.0 / 1024.0 << "MiB" << endl;
    cerr << "Time use:" << (double)clock() / CLOCKS_PER_SEC * 1000.0 << "ms" << endl;
#endif
    return 0;
}