题目大意
给定两棵有根树,分别有
n,m
个节点。
你只能在某个节点下面添加节点。求最少添加多少个节点能使两棵树同构。
1≤n,m≤200
题目分析
设
fi,j
表示第一棵树中点
x
匹配第二棵树中点
考虑递归处理
fx,y
,如果我们将
x
和
时间复杂度
O(n3)
(这样就钦定了费用流时间复杂度
O(1)
了?QwQ)。
代码实现
好久没有打 zkw 费用流了~
#include <iostream>
#include <climits>
#include <cstring>
#include <cstdio>
#include <cctype>
using namespace std;
const int INF=INT_MAX/3;
const int N=205;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
struct network
{
static const int S=0;
static const int T=N*2;
static const int M=N*N+N*2;
int f[M],r[M],next[M],tov[M],c[M];
int last[T+5],d[T+5],now[T+5];
bool vis[T+5];
int tot,mincost;
void insert(int x,int y,int Vol,int Cst,int Rev){tov[++tot]=y,f[tot]=Vol,c[tot]=Cst,r[tot]=tot+Rev,next[tot]=last[x],last[x]=tot;}
void addedge(int x,int y,int Vol,int Cst){insert(x,y,Vol,Cst,1),insert(y,x,0,-Cst,-1);}
bool relabel()
{
int tmp=INF;
for (int x=S;x<=T;x++)
if (vis[x])
for (int i=last[x],y;i;i=next[i])
if (!vis[y=tov[i]]&&f[i]>0) tmp=min(tmp,d[y]-d[x]+c[i]);
if (tmp==INF) return 0;
for (int x=S;x<=T;x++)
if (vis[x]) vis[x]=false,d[x]+=tmp;
return 1;
}
int aug(int x,int flow)
{
if (x==T) return mincost+=d[0]*flow,flow;
vis[x]=true;
int ret=0;
for (int i=now[x],y;i;i=next[i])
if (d[y=tov[i]]+c[i]==d[x]&&f[i]>0&&!vis[y])
{
int tmp=aug(y,min(flow,f[i]));
if (tmp)
{
ret+=tmp,f[i]-=tmp,f[r[i]]+=tmp,flow-=tmp;
now[x]=i;
if (!flow) break;
}
}
return ret;
}
int mincost_maxflow()
{
mincost=0;
do
{
for (int x=S;x<=T;x++) now[x]=last[x];
do {}
while (aug(S,INF));
}while (relabel());
return mincost;
}
void clear()
{
for (;tot;tot--) f[tot]=c[tot]=next[tot]=tov[tot]=r[tot]=0;
for (int i=S;i<=T;i++) vis[i]=false,d[i]=last[i]=now[i]=0;
mincost=0;
}
}G;
struct tree
{
int size[N+5],last[N+5],tov[N+5],next[N+5],fa[N+5];
int tot;
void insert(int x,int y){tov[++tot]=y,next[tot]=last[x],last[x]=tot;}
void dfs(int x)
{
size[x]=1;
for (int i=last[x],y;i;i=next[i])
dfs(y=tov[i]),size[x]+=size[y];
}
}t[2];
int f[N+5][N+5],son[2][N+5];
int n[2];
void calc(int x0,int x1)
{
for (int i=t[0].last[x0];i;i=t[0].next[i])
for (int j=t[1].last[x1];j;j=t[1].next[j])
calc(t[0].tov[i],t[1].tov[j]);
for (;son[0][0];) son[0][son[0][0]--]=0;
son[0][0]=0;
for (int i=t[0].last[x0];i;i=t[0].next[i]) son[0][++son[0][0]]=t[0].tov[i];
for (;son[1][0];) son[1][son[1][0]--]=0;
son[1][0]=0;
for (int i=t[1].last[x1];i;i=t[1].next[i]) son[1][++son[1][0]]=t[1].tov[i];
son[0][0]>son[1][0]?son[1][0]=son[0][0]:son[0][0]=son[1][0];
G.clear();
for (int i=1;i<=son[0][0];i++) G.addedge(G.S,i,1,0);
for (int i=1;i<=son[1][0];i++) G.addedge(i+son[0][0],G.T,1,0);
for (int i=1;i<=son[0][0];i++)
for (int j=1;j<=son[1][0];j++)
G.addedge(i,j+son[0][0],1,f[son[0][i]][son[1][j]]);
f[x0][x1]=G.mincost_maxflow();
}
void pre()
{
for (int i=1;i<=n[0];i++) f[i][0]=t[0].size[i];
for (int i=1;i<=n[1];i++) f[0][i]=t[1].size[i];
}
int main()
{
freopen("trees.in","r",stdin),freopen("trees.out","w",stdout);
for (int i=0;i<=1;i++)
{
n[i]=read()+1;
for (int j=2;j<=n[i];j++)
t[i].fa[j]=read()+1,t[i].insert(t[i].fa[j],j);
t[i].dfs(1);
}
pre();
calc(1,1);
printf("%d\n",f[1][1]);
fclose(stdin),fclose(stdout);
return 0;
}