J Identical Trees
https://www.cnblogs.com/lesning/p/13476608.html
神奇的树形DP+二分图最大权值匹配转移

dp[x][y]表示以左边的树x为根,右边的树y为根,他们有dp[x][y]个序号是重合的,若x和y不同构那就dp[x][y] = -INF;

第一步利用get函数获得每一种树的形式编号,cnt1[x] = cnt2[y]就代表x和y同构,
第二步就跑费用流,每次跑都重新建图(删除之前建过的图)

如何转移?
给x的儿子们和y的儿子们建个二分图跑最大权值匹配,跑下来的最大权值就是儿子们的答案。比赛时候感觉复杂度有点玄学不敢写罢了,

本人代码弱智,易于理解,详细的看代码把,

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 3020;
int n, m;
queue<int> que;
int head[maxn];

unordered_map<string, int>id;

struct Edge {
    int id, nex;
    int w, f;
}edge[40400];

int cnt = 0;


void init() {
    cnt = 0;
    memset(head, -1, sizeof(head));
    while (que.size()) que.pop();
}

void add_(int x, int y, int f, int w) {
    edge[cnt].id = y;
    edge[cnt].f = f;
    edge[cnt].w = w;
    edge[cnt].nex = head[x];
    head[x] = cnt++;
}

void add(int x, int y, int f, int w) {
    add_(x, y, f, w);
    add_(y, x, 0, -w);
}
int vis[maxn];
int dis[maxn];
int mflow[maxn];
int per[maxn];

int spfa(int s, int t) {
    memset(vis, 0, sizeof(vis));
    memset(dis, inf, sizeof(dis));
    mflow[s] = inf;
    que.push(s);
    dis[s] = 0;
    vis[s] = 1;
    while (que.size()) {
        int x = que.front();
        que.pop();
        vis[x] = 0;
        for (int i = head[x]; ~i; i = edge[i].nex) {
            int v = edge[i].id;
            if (dis[v] > dis[x] + edge[i].w && edge[i].f) {
                dis[v] = dis[x] + edge[i].w;
                per[v] = i;
                mflow[v] = min(mflow[x], edge[i].f);
                if (vis[v]) continue;

                vis[v] = 1;
                que.push(v);
            }
        }
    }
    if (dis[t] != inf)  return 1;
    else return 0;
}

void update(int s, int t, int& flow) {
    int minn = mflow[t];
    for (int i = t; i != s; i = edge[per[i] ^ 1].id) {

        int x = per[i];
        edge[x].f -= minn;
        edge[x ^ 1].f += minn;
    }
    flow += minn;
}

int solve(int s, int t, int& flow) {
    int ans = 0;
    while (spfa(s, t)) {//用spfa开路
        ans += dis[t] * mflow[t];
        update(s, t, flow);
    }
    return ans;
}


ll dp[550][550];
vector<int>G1[maxn], G2[maxn];
int jude[550][550];
int aa = 0;
int cnt1[550], cnt2[550];


int dfs(int x, int y) {
    //看看x和y是否同构
    if (jude[x][y] == 0) {
        dp[x][y] = -inf;
        return 0;
    }


    if (x == y) {
        dp[x][y] = 1;
        if (G1[x].size() == 0) return 0;
    }

    for (int i = 0; i < G1[x].size(); i++) {
        for (int j = 0; j < G2[y].size(); j++) {
            int a = G1[x][i];
            int b = G2[y][j];
            dfs(a, b);
        }
    }
    init();

    for (int i = 0; i < G1[x].size(); i++) add(2010, i, 1, 0);
    for (int j = 0; j < G2[y].size(); j++) add(j + 505, 2011, 1, 0);

    for (int i = 0; i < G1[x].size(); i++) {
        for (int j = 0; j < G2[y].size(); j++) {
            int a = G1[x][i];
            int b = G2[y][j];
            add(i, j + 505, 1, -dp[a][b]);
        }
    }
    int cns = 0;

    int ans = solve(2010, 2011, cns);
    dp[x][y] -= ans;

    //cout << x << "----------" << y << ":\t" << dp[x][y] <<"\t"<< cns << endl;
    return 0;
}

int get(int x, int y) {

    for (int i = 0; i < G1[x].size(); i++) {
        for (int j = 0; j < G2[y].size(); j++) {
            int a = G1[x][i];
            int b = G2[y][j];
            get(a, b);
        }
    }
    string sn;
    if (G1[x].size() == G2[y].size()) {
        if (G1[x].size() == 0) {
            jude[x][y] = 1;
        }
        else {
            for (int p : G1[x]) {
                sn += (char)(cnt1[p] + '0');
            }
            sort(sn.begin(), sn.end());
            if (id[sn] == 0) id[sn] = ++aa;
            cnt1[x] = id[sn];
            sn.clear();

            for (int p : G2[y]) {
                sn += (char)(cnt2[p] + '0');
            }
            sort(sn.begin(), sn.end());
            if (id[sn] == 0) id[sn] = ++aa;
            cnt2[y] = id[sn];
            sn.clear();

            if (cnt1[x] == cnt2[y]) jude[x][y] = 1;
        }
    }

    return 0;
}
int main() {
    scanf("%d", &n);
    int root1, root2;
    int x;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &x);
        if (x == 0) root1 = i;
        else G1[x].push_back(i);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &x);
        if (x == 0) root2 = i;
        else G2[x].push_back(i);
    }

    for (int i = 1; i <= n; i++) dp[i][i] = 1;
    get(root1, root2);

    dfs(root1, root2);

    cout << n - dp[root1][root2] << endl;
}
/*
7
0 1 1 2 2 3 3
0 1 1 3 3 2 2
输出2


*/