分析
看懂题意后思路会较为清晰虽然我也讲不清楚,请dalao们感性理解
针对题目的动态根,我们可以考虑两棵树同用一个根,然后同构树有个神奇的性质:如果使强制使两棵树的重心为根,那么这两棵树同构当且仅当这两棵有根树同构
但是重心可能有两个呢,我们只需要断开两个重心之间的边,然后新建一个点连向两个重心,新点就是新重心了
我们可以想到树中同深度同构的部分可以交换以减少代价
同构?树哈希咯
处理出每个点的哈希值后,我们设f[u][v]为以u为根的子树和以v为根的子树同构,且完成u子树的初始状态到v子树的最终状态的最小代价
显然这个要从他们的儿子处转移上来
然后我们可以发现,转移的过程就是选择这两棵子树中的若干对点,选择一对的代价为f[su][sv],使总代价最小
这是个带权二分图,可用KM算法或费用流算法解决
建议使用KM,费用流算法需要注意空间大小,代码复杂
#include <iostream> #include <cstdio> #include <memory.h> #include <algorithm> #include <queue> using namespace std; const int N=7e2+10; const int P=76447; struct Graph { int u,v,nx; }g[2*N]; struct Cube { int u,v,nx,c,w; }t[2*N*N+4*N]; int cnt,list[N],vis[2*N],dep[N],flow[2*N],dis[2*N],fa[2*N]; int g1,g2,sz[N],mxsize; int h[N],xx[N],f[N][N],p[N]; int n,from[N],to[N],ans; void Add(int u,int v) { g[++cnt]=(Graph){u,v,list[u]};list[u]=cnt; g[++cnt]=(Graph){v,u,list[v]};list[v]=cnt; } void Add_Cube(int u,int v,int w,int c) { t[++cnt]=(Cube){u,v,flow[u],c,w};flow[u]=cnt; t[++cnt]=(Cube){v,u,flow[v],0,-w};flow[v]=cnt; } void Find_G(int u,int f) { int size=0; sz[u]=1; for (int i=list[u];i;i=g[i].nx) if (g[i].v!=f) { Find_G(g[i].v,u); sz[u]+=sz[g[i].v]; size=max(size,sz[g[i].v]); } size=max(size,n-sz[u]); if (size==mxsize) g2=u; if (size<mxsize) { mxsize=size; g1=u; g2=0; } } void Tree_Hash(int u,int f) { int xcnt=0;dep[u]=dep[f]+1; for (int i=list[u];i;i=g[i].nx) if (g[i].v!=f) Tree_Hash(g[i].v,u); for (int i=list[u];i;i=g[i].nx) if (g[i].v!=f) xx[++xcnt]=h[g[i].v]; sort(xx+1,xx+xcnt+1); h[u]=17731; for (int i=1;i<=xcnt;i++) h[u]=(h[u]*7441%P+xx[i])%P; h[u]=h[u]*3119%P; } bool CMP(int u,int v) { return dep[u]!=dep[v]?dep[u]>dep[v]:h[u]<h[v]; } bool SPFA() { queue<int> q; while (!q.empty()) q.pop(); memset(dis,0x3f,sizeof dis); dis[0]=0;q.push(0); while (!q.empty()) { int u=q.front();q.pop(); for (int i=flow[u];i;i=t[i].nx) if (dis[t[i].v]>dis[u]+t[i].w&&t[i].c) { dis[t[i].v]=dis[u]+t[i].w; fa[t[i].v]=i; if (!vis[t[i].v]) q.push(t[i].v); vis[t[i].v]=1; } vis[u]=0; } return dis[2*n+1]!=0x3f3f3f3f; } void Get_Ans() { int x=2*n+1,mf=2147483647; while (fa[x]) { ans+=t[fa[x]].w; mf=min(mf,t[fa[x]].c); x=t[fa[x]].u; } x=2*n+1; while (fa[x]) { t[fa[x]].c-=mf; t[fa[x]^1].c+=mf; x=t[fa[x]].u; } } int Min_Cost_Flow() { ans=0; while (SPFA()) Get_Ans(); return ans; } void DP() { for (int i=1;i<=n;i++) p[i]=i; sort(p+1,p+n+1,CMP); for (int i=1;i<=n;) { int j=i; for (;j<=n;j++) if (dep[p[i]]!=dep[p[j]]||h[p[i]]!=h[p[j]]) break; for (int k=i;k<j;k++) for (int l=i;l<j;l++) { int u=p[k],v=p[l]; cnt=1;memset(flow,0,sizeof flow); for (int sv=list[v];sv;sv=g[sv].nx) if (dep[g[sv].v]==dep[v]+1) Add_Cube(n+g[sv].v,2*n+1,0,1); for (int su=list[u];su;su=g[su].nx) if (dep[g[su].v]==dep[u]+1) { Add_Cube(0,g[su].v,0,1); for (int sv=list[v];sv;sv=g[sv].nx) if (dep[g[sv].v]==dep[v]+1&&h[g[su].v]==h[g[sv].v]) Add_Cube(g[su].v,n+g[sv].v,f[g[su].v][g[sv].v],1); } f[u][v]=Min_Cost_Flow()+(from[u]!=to[v]); } i=j; } } int main() { scanf("%d",&n); for (int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),Add(u,v); for (int i=1;i<=n;i++) scanf("%d",&from[i]); for (int i=1;i<=n;i++) scanf("%d",&to[i]); mxsize=n; Find_G(1,0); if (g2) { cnt=0;memset(list,0,sizeof list); for (int i=1,j=1;i<=n-1;i++,j=i*2-1) if ((g[j].u!=g1||g[j].v!=g2)&&(g[j].v!=g1||g[j].u!=g2)) Add(g[j].u,g[j].v); Add(g1,n+1),Add(n+1,g2); n++; g1=n; } Tree_Hash(g1,0); DP(); printf("%d",f[g1][g1]); }