(题目背景吐槽:圣殿骑士团,在14世纪初被灭,根本不可能出现在1486年。)
灵感来自:https://blog.csdn.net/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; }