题目

题目大意

给你一棵树,树上每个节点有000或111的状态。
用最少的操作次数使得当前状态与目标状态同构。


思考历程

首先想到的是找重心。
因为根是不确定的,但重心只会有一个或两个,以重心为根就能方便很多。
如果重心有两个,就将连接它们的边拆成点,让它们分别与这个点相连就好了(重心是连在一起的)。
然后就是树上哈希……
哈希之后就开始了艰辛的思考历程……
于是比赛就结束了。


正解

我前面的思考是没有问题的。
后面的该怎么处理?当然是DP啊!
设fi,jf_{i,j}fi,j​表示子树iii对应子树jjj的最小操作数。
显然如果iii能匹配jjj,就必须要保证它们的哈希值相等。
但是还会有其它条件,综合起来就是它们祖先的哈希值也相等……
其实为了简化操作,我们再保证它们的深度相等就行了,虽然这并不能保证它们真的能够匹配,但也就算了吧……(我曾试过将完全出去这些冗余状态,但程序跑得更慢了,这意味着数据的这一类冗余状态并不多)
所以可以将点按照深度和哈希值排序,从后往前做。
接下来考虑转移,首先aixorbja_i \ xor \ b_jai​ xor bj​是一定要加上的、
然后就将各自的子树两两配对,也就是∑fx,y\sum f_{x,y}∑fx,y​,其中xxx是iii的儿子,yyy是jjj的儿子,而且xxx和yyy哈希值相等。
我们要保证这个东西最小。
于是这就变成了二分图的最小权完备匹配。
题目说每个点的度数小于等于111111,这意味着看起来能够状压DP。
但实际上,如果不非常用力地开常数,那状压DP是很难卡过去的……(我打了80分)。
于是就可以用费用流或KM算法(因为这题求的是最小权,所以将边权取相反数就可以了)。
时间复杂度为玄学……


代码

状压DP


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 710
#define mod 1000000007
inline void get_min(int &a,int b){a>b?a=b:0;
}
int n;
int e[N][11],ne[N];
int bz[N][11],nbz;
int siz[N];
int tmpr[2],cnt,root;
void get_siz(int x,int fa){bool is_root=1;siz[x]=1;for (int i=0;i<ne[x];++i){int y=e[x][i];if (y!=fa){get_siz(y,x);siz[x]+=siz[y];if (y!=fa)is_root&=(siz[y]<<1<=n);}}is_root&=(n-siz[x]<<1<=n);if (is_root)tmpr[cnt++]=x;
}
int fa[N],dep[N];
long long powd[N],h[N];
inline bool cmpe(int x,int y){return siz[x]<siz[y] || siz[x]==siz[y] && h[x]<h[y];
}
void get_hash(int x){dep[x]=dep[fa[x]]+1;siz[x]=1;for (int i=0;i<ne[x];++i){int y=e[x][i];if (y!=fa[x]){fa[y]=x;get_hash(y);siz[x]+=siz[y];}else{for (int j=i;j<ne[x]-1;++j)e[x][j]=e[x][j+1];ne[x]--;--i;}}sort(e[x],e[x]+ne[x],cmpe);h[x]=siz[x];int w=1;for (int i=0;i<ne[x];++i){int y=e[x][i];h[x]=(h[x]+h[y]*powd[w])%mod;w+=siz[y];}
}
int q[N];
inline bool cmpq(int x,int y){return dep[x]<dep[y] || dep[x]==dep[y] && h[x]<h[y];
}
int f[N][N],g[13][2048];
//int bg[13][13];
int a[N],b[N];
int main(){freopen("in.txt","r",stdin);scanf("%d",&n);for (int i=1;i<n;++i){int u,v;scanf("%d%d",&u,&v);e[u][ne[u]++]=v;e[v][ne[v]++]=u;}for (int i=1;i<=n;++i)scanf("%d",&a[i]);for (int i=1;i<=n;++i)scanf("%d",&b[i]);get_siz(1,0);if (cnt==2){int u=tmpr[0],v=tmpr[1];root=++n;for (int i=0;i<ne[u];++i)if (e[u][i]==v){e[u][i]=root;break;}for (int i=0;i<ne[v];++i)if (e[v][i]==u){e[v][i]=root;break;}e[root][0]=u,e[root][1]=v,ne[root]=2;}elseroot=tmpr[0];powd[0]=1;for (int i=1;i<=n;++i)powd[i]=powd[i-1]*997%mod;get_hash(root);for (int i=1;i<=n;++i)q[i]=i;sort(q+1,q+n+1,cmpq);memset(f,63,sizeof f);for (int i=n,r=n,ii=q[i];i>=1;ii=q[--i]){for (int j=i,jj=q[j];j>=1 && dep[ii]==dep[jj] && h[ii]==h[jj];jj=q[--j]){memset(g,63,sizeof g);g[0][0]=0;for (int x=0;x<ne[ii];++x){int xx=e[ii][x];for (int k=0;k<1<<ne[ii];++k)for (int y=0;y<ne[jj];++y){if (k>>y&1)continue;get_min(g[x+1][k|1<<y],g[x][k]+f[xx][e[jj][y]]);}}f[ii][jj]=g[ne[ii]][(1<<ne[ii])-1]+(a[ii]^b[jj]);if (ii==jj)continue;memset(g,63,sizeof g);g[0][0]=0;for (int y=0;y<ne[jj];++y){int yy=e[jj][y];for (int k=0;k<1<<ne[jj];++k)for (int x=0;x<ne[ii];++x){if (k>>x&1)continue;get_min(g[y+1][k|1<<x],g[y][k]+f[yy][e[ii][x]]);}}f[jj][ii]=g[ne[jj]][(1<<ne[jj])-1]+(a[jj]^b[ii]);}}printf("%d\n",f[root][root]);return 0;
}

KM算法

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 710
#define mod 1000000007
inline void get_min(int &a,int b){a>b?a=b:0;
}
int n;
int e[N][11],ne[N];
int bz[N][11],nbz;
int siz[N];
int tmpr[2],cnt,root;
void get_siz(int x,int fa){bool is_root=1;siz[x]=1;for (int i=0;i<ne[x];++i){int y=e[x][i];if (y!=fa){get_siz(y,x);siz[x]+=siz[y];if (y!=fa)is_root&=(siz[y]<<1<=n);}}is_root&=(n-siz[x]<<1<=n);if (is_root)tmpr[cnt++]=x;
}
int fa[N],dep[N];
long long powd[N],h[N];
inline bool cmpe(int x,int y){return siz[x]<siz[y] || siz[x]==siz[y] && h[x]<h[y];
}
void get_hash(int x){dep[x]=dep[fa[x]]+1;siz[x]=1;for (int i=0;i<ne[x];++i){int y=e[x][i];if (y!=fa[x]){fa[y]=x;get_hash(y);siz[x]+=siz[y];}else{for (int j=i;j<ne[x]-1;++j)e[x][j]=e[x][j+1];ne[x]--;--i;}}sort(e[x],e[x]+ne[x],cmpe);h[x]=siz[x];int w=1;for (int i=0;i<ne[x];++i){int y=e[x][i];h[x]=(h[x]+h[y]*powd[w])%mod;w+=siz[y];}
}
int q[N];
inline bool cmpq(int x,int y){return dep[x]<dep[y] || dep[x]==dep[y] && h[x]<h[y];
}
int f[N][N];
int m;
int bg[13][13];
int exl[13],exr[13];
bool visl[13],visr[13];
int bel[13];
bool find(int x){visl[x]=1;for (int i=0;i<m;++i)if (exl[x]+exr[i]==bg[x][i] && !visr[i]){visr[i]=1;if (bel[i]==-1 || find(bel[i])){bel[i]=x;return 1;}}return 0;
}
inline int km(){memset(bel,255,sizeof bel);for (int i=0;i<m;++i){exl[i]=bg[i][0];for (int j=1;j<m;++j)exl[i]=max(exl[i],bg[i][j]);}memset(exr,0,sizeof exr);for (int k=0;k<m;++k){while (1){memset(visl,0,sizeof visl);memset(visr,0,sizeof visr);if (find(k))break;int d=0x3f3f3f3f;for (int i=0;i<m;++i)if (visl[i])for (int j=0;j<m;++j)if (!visr[j])d=min(d,exl[i]+exr[j]-bg[i][j]);for (int i=0;i<m;++i)if (visl[i])exl[i]-=d;for (int j=0;j<m;++j)if (visr[j])exr[j]+=d;}}int res=0;for (int i=0;i<m;++i)res+=bg[bel[i]][i];return -res;
}
int a[N],b[N];
inline void calc(int ii,int jj){for (int x=0;x<ne[ii];++x){int xx=e[ii][x];for (int y=0;y<ne[jj];++y)bg[x][y]=-f[xx][e[jj][y]];}m=ne[ii];f[ii][jj]=km()+(a[ii]^b[jj]);
}
int main(){freopen("in.txt","r",stdin);scanf("%d",&n);for (int i=1;i<n;++i){int u,v;scanf("%d%d",&u,&v);e[u][ne[u]++]=v;e[v][ne[v]++]=u;}for (int i=1;i<=n;++i)scanf("%d",&a[i]);for (int i=1;i<=n;++i)scanf("%d",&b[i]);get_siz(1,0);if (cnt==2){int u=tmpr[0],v=tmpr[1];root=++n;for (int i=0;i<ne[u];++i)if (e[u][i]==v){e[u][i]=root;break;}for (int i=0;i<ne[v];++i)if (e[v][i]==u){e[v][i]=root;break;}e[root][0]=u,e[root][1]=v,ne[root]=2;}elseroot=tmpr[0];powd[0]=1;for (int i=1;i<=n;++i)powd[i]=powd[i-1]*997%mod;get_hash(root);for (int i=1;i<=n;++i)q[i]=i;sort(q+1,q+n+1,cmpq);memset(f,63,sizeof f);for (int i=n,r=n,ii=q[i];i>=1;ii=q[--i]){for (int j=i,jj=q[j];j>=1 && dep[ii]==dep[jj] && h[ii]==h[jj];jj=q[--j]){calc(ii,jj);if (ii!=jj)calc(jj,ii);}}printf("%d\n",f[root][root]);return 0;
}

总结

想不出来的东西就用网络流吧……

[JZOJ3296] 【SDOI2013】刺客信条相关推荐

  1. [SDOI2013]刺客信条

    Description 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的天赋,成为了杰出的刺 ...

  2. BZOJ3197:[SDOI2013]刺客信条——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3197 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受 ...

  3. [BZOJ3197][SDOI2013]刺客信条assassin

    bzoj luogu Description 故事发生在1486 年的意大利,Ezio原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的 ...

  4. 【JZOJ3296】【SDOI2013】刺客信条(assassin)

    ╰( ̄▽ ̄)╭ Description 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的天赋 ...

  5. JZOJ3296. 【SDOI2013】刺客信条

    Description 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的天赋,成为了杰出的刺 ...

  6. [哈希][费用流]JZOJ 3296 【SDOI2013】刺客信条

    Description 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的天赋,成为了杰出的刺 ...

  7. 【JZOJ3296】【BZOJ3197】【luoguP3296】刺客信条

    description 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的天赋,成为了杰出的刺 ...

  8. 刺客信条奥德赛无法加载库_点评刺客信条起源、奥德赛、英灵殿,哪个最好玩?...

    刺客信条起源 虽然大多数玩家一直诟病刺客信条系列的高重复性,在我看来,<刺客信条:起源>是育碧将刺客信条系列转向RPG的一大艺术品.这款游戏让我感到最惊讶的是世界氛围的营造.它的历史感以及 ...

  9. luogu P3306 [SDOI2013] 随机数生成器(BSGS,数列求通项,毒瘤特判)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 发个水题的 题解证明我还在() luogu P3306 [SDOI2013] 随机数生成器 Webli ...

  10. bzoj3122 [SDOI2013]随机数生成器

    bzoj3122 [SDOI2013]随机数生成器 给定一个递推式, \(X_i=(aX_{i-1}+b)\mod P\) 求满足 \(X_k=t\) 的最小整数解,无解输出 \(-1\) \(0\l ...

最新文章

  1. matlab 流程计算方法,吸波材料LLG公式计算复磁导率的过程及matlab程序
  2. sys.modules[__name__]的一个实例
  3. C++基础知识(五)—— 基本输入输出
  4. Java2实用教程(第二版)程序代码——第十四章 Component类的常用方法
  5. rk3399_android7.1的HDMI显示实现固定分辨率
  6. python GUI编程
  7. 安装VMware-打开时显示文件包含病毒
  8. 了解uni-app只需这一篇就足够了
  9. python关于模块说法错误的是_python常用模块错题
  10. 服务器的远程无法断开,远程桌面客户端断开连接且无法重新连接到同一会话
  11. excel单元格斜线_掌握Excel的这个制表技巧,“小白老师”也能变“超神”!
  12. oracle起监听命令,lsnrctl oracle 监听器 命令行 操作命令 lsnrctl services
  13. 行业分析是什么?该怎么做?
  14. 如何从源代码中下载图片
  15. 中国联通和中国电信如合并要抗衡中国移动需要时间
  16. P1478 陶陶摘苹果(升级版)
  17. Oracle 从垃圾箱中恢复数据以及清空垃圾箱
  18. python网络爬虫抹除webdriver指纹绕过淘宝滑块验证登录
  19. C51单片机之点亮LED灯
  20. 【Office】Excel如何导出宏

热门文章

  1. 关于OBS录制win10视频,全屏卡顿的问题
  2. 蓝牙驱动卸载后自动安装_外星人的控制中心下载,安装及常见问题处理方法
  3. [渝粤教育] 西南石油大学 岩体力学 参考 资料
  4. 微信H5分享钉钉分享设置方法
  5. ora-600[6002]解决
  6. 怎么修改html游戏存档,星露谷物语存档修改方法 如何修改游戏存档
  7. opencv的下载与安装
  8. 学科竞赛管理系统服务器错误,大学生学科竞赛管理系统的设计与实现
  9. 关于MDK调试stm32f072vbt6(HAL库)内部flash在擦除仿真时,卡死在擦除函数,同时弹出“Cannot access target.shutting down debug sessi
  10. 超简单!使用jQuery实现登录页面的“记住密码”功能