(题目背景吐槽:圣殿骑士团,在14世纪初被灭,根本不可能出现在1486年。)
灵感来自:https://blog.youkuaiyun.com/weixin_30932215/article/details/98347121
正解:
题目大意即:可以将一颗树随便转,要和原来的树同构,然后将两棵树对应的点异或之和取min。
确定原来树的重心为根,这样新的树的根就确定了。若重心有两个,则删掉两点连边,新开一个根节点(重心)连接两重心。(这样处理比较方便)
关于树哈希(详见oi-wiki),先存下来原先的树,后面依次搞新的树,要开long long。
枚举新的树的根,每次都做dp。f[x][y]表示 新的树以x为根的子树 与 原树以y为根的子树 相对应的异或之和。
对于每对x,y(意义同上),如何选择各自儿子的匹配?由于儿子<=11,可以用状压DP(或费用流)。g[a][b]表示匹配完了x的儿子的前i个,b表示y的儿子的匹配状态,最后用g[都匹配完]更新当前f[x][y]。
记得统计答案。
code如下,码量较大,逾200行,3000byte。
#include<bits/stdc++.h>
#define inf 1000000007
#define base 2333
#define mod (long long)((1e9)+7)
#define mid (l+r)/2
using namespace std;
int n,m,h[100005],cnt,a[1005],goal[1005],ans,fa[1005],g[15][(1<<11)+5];
int rt1,rt2,rt,size[1005],w[1005],f[1005][1005],N,fu[1005];
long long hosh[1005],hash[1005],tmp;
struct node{
int next,to,bz;
}e[100005];
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+ch-48;
ch=getchar();
}
return x*f;
}
void add(int x,int y)
{
//printf("Ok\n");
e[++cnt].to=y;
e[cnt].bz=1;
e[cnt].next=h[x];
h[x]=cnt;
}
void certroid(int x,int fa)
{
size[x]=1;
w[x]=0;
for(int i=h[x];i;i=e[i].next)
{
//printf("OK\n");
int y=e[i].to;
if(y!=fa)
{
certroid(y,x);
size[x]+=size[y];
w[x]=max(w[x],size[y]);
}
}
w[x]=max(w[x],n-size[x]);
//printf("node[%d]s=%d w=%d\n",x,size[x],w[x]);
if(w[x]<=n/2)
{
if(!rt1)rt1=x;
else rt2=x;
}
}
void hdfs(int x)
{
//printf("Ok %d %d\n",x,fa[x]);
int tot=0;
long long ans,a[15];
size[x]=1;hash[x]=0;
for(int i=h[x];i;i=e[i].next)
{
int y=e[i].to;
if(y!=fa[x]&&e[i].bz)
{
fa[y]=x;
hdfs(y);
size[x]+=size[y];
a[++tot]=hash[y];
}
}
sort(a+1,a+tot+1);ans=0;
//if(x==4)printf("find %lld\n",ans);
for(int i=1;i<=tot;i++)
{
ans=ans*base+a[i];
}
ans=ans*base+size[x]+1;
hash[x]=ans;
}
void dp(int x,int u)
{
//printf("ber %d-%d %d-%d\n",x,fa[x],u,fa[u]);
f[x][u]=(a[x]^goal[u]);
int sona[15],sonb[15],la=0,lb=0;
for(int i=h[x];i;i=e[i].next)
{
int y=e[i].to;
if(y!=fa[x]&&e[i].bz)
{
sona[++la]=y;
}
}
if(!la)
{
return;
}
for(int i=h[u];i;i=e[i].next)
{
int y=e[i].to;
if(y!=fu[u]&&e[i].bz)
{
sonb[++lb]=y;
}
}
for(int i=1;i<=la;i++)
{
for(int j=1;j<=lb;j++)
{
if(hash[sona[i]]==hash[sonb[j]])
{
dp(sona[i],sonb[j]);
// if(x==1&&u==1)printf("f[%d][%d]=%d\n",sona[i],sonb[j],f[sona[i]][sonb[j]]);
}
}
}
memset(g,0x3f,sizeof(g));
int ber=g[0][0];g[0][0]=0;
for(int i=0;i<la;i++)
{
for(int s=0,ss;s<(1<<lb);s++)
{
if(g[i][s]>=ber)continue;
for(int j=1;j<=lb;j++)
{
//if(x==2&&u==3&&i==0&&j==1&&!s)printf("BER\n");
if(((1<<(j-1))&s)==0)
{
if(hash[sona[i+1]]==hosh[sonb[j]])
{
ss=s+(1<<(j-1));;
g[i+1][ss]=min(g[i+1][ss],g[i][s]+f[sona[i+1]][sonb[j]]);
}
}
}
}
}
f[x][u]+=g[la][(1<<lb)-1];
//if(x==2&&u==3)printf("F %d %d\n",f[2][3],f[1][4]);
}
int main ()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=read();cnt=1;
for(int i=1,x,y;i<n;i++)
{
x=read(),y=read();
add(x,y);add(y,x);
}
for(int i=1;i<=n;i++)
{
a[i]=read();
}
for(int i=1;i<=n;i++)
{
goal[i]=read();
}
certroid(1,1);
//printf("rt=%d %d\n",rt1,rt2);
if(rt2)
{
for(int i=h[rt1];i;i=e[i].next)
{
int y=e[i].to;
if(y==rt2)
{
e[i].bz=e[i^1].bz=0;
}
}
add(n+1,rt1);add(rt1,n+1);
add(n+1,rt2);add(rt2,n+1);
rt=N=n+1;
}else rt=rt1,N=n;
fa[rt]=rt;
hdfs(rt);
for(int i=1;i<=N;i++)
{
hosh[i]=hash[i];
//printf("ber=%lld\n",hosh[i]);
fu[i]=fa[i];
}
tmp=hosh[rt],ans=inf;
//printf("rt=%d\n",rt);
for(int i=1;i<=N;i++)
{
fa[i]=i;
//memset(size,0,sizeof(size));
hdfs(i);
long long ber=hash[i];
//printf("Ok %lld-%lld\n",ber,tmp);
if(ber==tmp)
{
//printf("OK %d\n",i);
memset(f,0x3f,sizeof(f));
dp(i,rt);
ans=min(ans,f[i][rt]);
}
}
printf("%d\n",ans);
return 0;
}