题目链接
我们从两个树的根开始递归下去,设
为T1的l为根的子树,和T2的r为根的子树的最小代价。
然后往上合并的时候相当于一个二分图最小匹配。
先用树hash判同构,然后用最小费用最大流找到最小代价即可。
ps:比赛的时候,一直再递归函数里面开vector,结果爆炸一直T,,,长记性。。。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pll pair<ll,ll>
#define pb push_back
#define wzh(x) cerr<<#x<<'='<<x<<endl;
const int MAX_N = 1005;
const int MAX_M = 50000;
int pr[N], ar[N], _cnt;
void PP() {
for (int i = 2; i < N; i++) {
if (!pr[i])ar[++_cnt] = i;
for (int j = 1; j <= _cnt && 1ll * ar[j]*i < N; j++) {
pr[ar[j]*i] = 1;
if (i % ar[j] == 0)break;
}
}
}
struct edge {
int v, c, w, next; // v 表示边的另一个顶点,c 表示当前剩余容量,w 表示单位流量费用
} ;
struct MCMF
{
edge e[MAX_M];
int p[MAX_N], s, t, eid; // s 表示源点,t 表示汇点,需要在进行 costflow 之前设置完毕
int n;
void init(int _n) {
n = _n;
s = 1;
t = _n;
for (int i = 0; i <= n; i++)p[i] = -1;
eid = 0;
}
void insert(int u, int v, int c, int w) {
e[eid].v = v;
e[eid].c = c;
e[eid].w = w;
e[eid].next = p[u];
p[u] = eid++;
}
void addedge(int u, int v, int c, int w) {
insert(u, v, c, w);
insert(v, u, 0, -w);
}
bool inq[MAX_N];
int d[MAX_N]; // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
int pre[MAX_N]; // 最短路中连向当前顶点的边的编号
bool spfa() { // 以源点 s 为起点计算单源最短路,如果不存在从 s 到 t 的路径则返回 false,否则返回 true
for (int i = 0; i <= n; i++)d[i] = 1e9, inq[i] = 0, pre[i] = -1;
d[s] = 0;
inq[s] = true;
queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = false;
for (int i = p[u]; i != -1; i = e[i].next) {
if (e[i].c) { //注意这个条件!!!spfa这里是以w求最短路的,但仍然不能忽略容量c的考虑!
int v = e[i].v;
if (d[u] + e[i].w < d[v]) {
d[v] = d[u] + e[i].w;
pre[v] = i;
if (!inq[v]) {
q.push(v);
inq[v] = true;
}
}
}
}
}
return pre[t] != -1;
}
int min_cost_flow() { // 计算最小费用最大流
int ret = 0; // 累加和
while (spfa()) {
int flow = 555;
for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
flow = min(e[pre[i]].c, flow); // 计算当前增广路上的最小流量
}
for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
e[pre[i]].c -= flow; //容量是一定要跟着变化的,毕竟要继续循环使用spfa来更新下一条“能走的(看容量)”最短路。
e[pre[i] ^ 1].c += flow;
ret += e[pre[i]].w * flow;
}
}
return ret;
}
} Ga;
int n, a[N], b[N], RA, RB;
vector<int>v[N], g[N];
int sz1[N],sz2[N];
const int inf=n;
unsigned long long H1[N], H2[N];
int al[555][555];
void gao(int x, int y) {
if(sz1[x]!=sz2[y])return ;
if(sz1[x]==1){
al[x][y]=(x!=y);
return;
}
for(auto l:v[x]){
for(auto r:g[y]){
gao(l,r);
}
}
Ga.init(2+v[x].size()+g[y].size());
int sum=2+v[x].size()+g[y].size();
for(int i=0;i<v[x].size();i++){
Ga.addedge(1,i+2,1,0);
for(int j=0;j<g[y].size();j++){
int dx=v[x][i];
int dy=g[y][j];
//if(al[dx][dy]!=inf){
Ga.addedge(i+2,j+1+v[x].size()+1,1,al[dx][dy]);
}
}
for(int i=0;i<g[y].size();i++){
Ga.addedge(i+1+v[x].size()+1,sum,1,0);
}
al[x][y] = min(Ga.min_cost_flow()+(x!=y),al[x][y]);
return ;
}
void check1(int x) {
sz1[x] = 1; H1[x] = 1;
if (!v[x].size()) {
H1[x] = 1;
return;
}
H1[x] = 0;
for (auto k : v[x]) {
check1(k);
sz1[x] += sz1[k];
}
for (auto k : v[x]) {
H1[x] += H1[k] * ar[sz1[k]];
}
H1[x] *= sz1[x];
}
void check2(int x) {
sz2[x] = 1; H2[x] = 1;
if (!g[x].size()) {
H2[x] = 1;
return;
}
H2[x] = 0;
for (auto k : g[x]) {
check2(k);
sz2[x] += sz2[k];
}
for (auto k : g[x]) {
H2[x] += H2[k] * ar[sz2[k]];
}
H2[x] *= sz2[x];
}
int main() {
ios::sync_with_stdio(false);
cin >> n; PP();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
al[i][j]=555;
}
}
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i])v[a[i]].pb(i);
else RA = i;
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
if (b[i])g[b[i]].pb(i);
else RB = i;
}
check1(RA);
check2(RB);
gao(RA, RB);
cout<<al[RA][RB]<<'\n';
//puts("ok");
return 0;
}
/*
6
2 3 0 2 6 3
2 3 0 2 3 5
*/