https://www.lydsy.com/JudgeOnline/problem.php?id=3197

故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客。最终,凭借着他的努力和出众的天赋,成为了杰出的刺客大师,他不仅是个身手敏捷的武林高手,飞檐走壁擅长各种暗杀术。刺客组织在他的带领下,为被剥削的平民声张正义,赶跑了原本统治意大利的圣殿骑士首领-教皇亚历山大六世。在他的一生中,经历了无数次惊心动魄、扣人心弦的探险和刺杀。

曾经有一次,为了寻找Altair 留下的线索和装备,Ezio 在佛罗伦萨中的刺客墓穴进行探索。这个刺客墓穴中有许多密室,且任何两个密室之间只存在一条唯一的路径。这些密室里都有一个刺客标记,他可以启动或者关闭该刺客标记。为了打开储存着线索和装备的储藏室,Ezio 必须操作刺客标记来揭开古老的封印。要想解开这个封印,他需要通过改变某些刺客标记的启动情况,使得所有刺客标记与封印密码“看起来一样”。

在这里,“看起来一样”的定义是:存在一种“标记”密室与“密码”密室之间一一对应的关系,使得密室间的连接情况和启动情况相同(提示中有更详细解释)。幸运的是,在Ezio 来到刺客墓穴之前,在Da Vinci 的帮助下,Ezio 已经得知了打开储藏室所需要的密码。

而你的任务则是帮助Ezio 找出达成目标所需要最少的改动标记次数。

参考:https://www.luogu.org/blog/user29936/solution-p3296

多半是树哈希判同构了。

设$f[u][v]$表示$u$子树和$v$子树同构且同层的情况下$u$子树原标记变动成$v$子树的新标记需要的最少次数。

那么实际上就是枚举$u$和$v$的儿子子树互相匹配,用他们的$f$转移到$f[u][v]$上,很明显这是一个带权二分图匹配的过程,KM是一个很好的选择。

如果要是枚举根来做的话,复杂度就是$O(1331n^2)$过不了,当然如果你使用动态换根的话,虽然我没有想过,但是到目前位置,代码已经快200行了,如果再动态换根的话就要累死了(当然debug就更累了)。

我们有个很妙的性质:取这棵树的重心(如果有两个重心,则将两个重心之间的边上建这个点,取这个点)作为根。

因为我们有一个美妙的结论:两棵树同构,当且仅当以两棵树重心为根的树同构。

(其实我也不知道为什么233可能是此时同构的对是最多的吧……)

#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
const int M=12;
const int N=705;
const int B=6662333;
const int INF=1e9;
inline int read(){int X=0,w=0;char ch=0;while(!isdigit(ch)){w|=ch=='-';ch=getchar();}while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();return w?-X:X;
}
int dis[M][M],wx[M],wy[M],match[M],sla[M];
bool vx[M],vy[M];
bool dfs2(int u,int n){vx[u]=1;for(int v=1;v<=n;v++){       if(!vy[v]){int w=wx[u]+wy[v]-dis[u][v];if(!w){vy[v]=1;if(!match[v]||dfs2(match[v],n)){match[v]=u;return 1;}}else sla[v]=min(sla[v],w);}}return 0;
}
int KM(int n){memset(wx,-127,sizeof(wx));memset(wy,-127,sizeof(wy));memset(match,0,sizeof(match));for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)wx[i]=max(wx[i],dis[i][j]);for(int i=1;i<=n;i++){memset(sla,127,sizeof(sla));while(1){memset(vx,0,sizeof(vx));memset(vy,0,sizeof(vy));if(dfs2(i,n))break;int minn=INF;for(int j=1;j<=n;j++)if(!vy[j])minn=min(minn,sla[j]);for(int j=1;j<=n;j++){if(vx[j])wx[j]-=minn;if(vy[j])wy[j]+=minn;else sla[j]-=minn;}}}int ans=0;for(int i=1;i<=n;i++)if(dis[match[i]][i]!=-INF)ans-=dis[match[i]][i];return ans;
}
struct node{int to,nxt;
}e[N*2];
int n,cnt,head[N],fa[N],a[N],b[N],q[N],size[N],son[N],dep[N];
ll h[N];
inline void add(int u,int v){e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
}
int calcg(int st){int r=0,g1,g2=0,maxn=n;q[++r]=st;fa[st]=0;for(int l=1;l<=r;l++){int u=q[l];size[u]=1;son[u]=0;for(int i=head[u];i!=-1;i=e[i].nxt){int v=e[i].to;if(v==fa[u])continue;fa[v]=u;q[++r]=v;}}for(int l=r;l>=1;l--){int u=q[l],v=fa[u];if(r-size[u]>son[u])son[u]=r-size[u];if(son[u]<maxn)g1=u,maxn=son[u],g2=0;else if(son[u]==maxn)g2=u;if(!v)break;size[v]+=size[u];if(size[u]>son[v])son[v]=size[u];}if(!g2)return g1;for(int i=head[g1];i!=-1;i=e[i].nxt){int v=e[i].to;if(v==g2){e[i].to=++n;e[i^1].to=n;add(n,g1);add(n,g2);return n;}}
}
ll dfs1(int u){ll num[N];int r=0;size[u]=1;h[u]=B;for(int i=head[u];i!=-1;i=e[i].nxt){int v=e[i].to;if(v==fa[u])continue;fa[v]=u;dep[v]=dep[u]+1;num[++r]=dfs1(v);size[u]+=size[v];}sort(num+1,num+r+1);for(int i=1;i<=r;i++)h[u]=h[u]*B+num[i];return h[u]*=size[u];
}
int f[N][N],to1[N],to2[N];
int find(int u1,int u2){int idx1=0,idx2=0;memset(to1,0,sizeof(to1));memset(to2,0,sizeof(to2));for(int i=head[u1];i!=-1;i=e[i].nxt){int v1=e[i].to;if(v1==fa[u1])continue;if(!to1[v1])to1[v1]=++idx1;for(int j=head[u2];j!=-1;j=e[j].nxt){int v2=e[j].to;if(v2==fa[u2])continue;if(!to2[v2])to2[v2]=++idx2;//printf("1 %d %d %d\n",v1,v2,f[v1][v2]);dis[to1[v1]][to2[v2]]=-f[v1][v2];}}return KM(idx1)+(a[u1]!=b[u2]);
}
int bfs(int s){int r=0;q[++r]=s;for(int l=0;l<=r;l++){int u=q[l];for(int i=head[u];i!=-1;i=e[i].nxt){int v=e[i].to;if(v==fa[u])continue;q[++r]=v;}}for(int i=r;i>=0;i--){if(dep[q[i]]!=dep[q[r]]){i++;for(int j=r;j>=i;j--)for(int k=r;k>=i;k--)if(h[q[j]]==h[q[k]]){f[q[j]][q[k]]=find(q[j],q[k]);//printf("%d %d %d\n",q[j],q[k],f[q[j]][q[k]]);
            }r=i-1;}}return find(s,s);
}
int main(){cnt=-1,memset(head,-1,sizeof(head));n=read();for(int i=1;i<n;i++){int u=read(),v=read();add(u,v);add(v,u);}for(int i=1;i<=n;i++)a[i]=read();for(int i=1;i<=n;i++)b[i]=read();int rt=calcg(1);fa[rt]=0;dfs1(rt);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j]=INF;printf("%d\n",bfs(rt));return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

转载于:https://www.cnblogs.com/luyouqi233/p/9192742.html

BZOJ3197:[SDOI2013]刺客信条——题解相关推荐

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

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

  2. [BZOJ3197][Sdoi2013]assassin(树形DP+树同构+二分图最优匹配)

    关于树同构,有一个神奇的性质: 一棵树的重心只有 111 个或 2" role="presentation" style="position: relative ...

  3. [SDOI2013]刺客信条

    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. luogu P3306 [SDOI2013] 随机数生成器(BSGS,数列求通项,毒瘤特判)

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

  9. BZOJ3123: [Sdoi2013]森林

    BZOJ3123: [Sdoi2013]森林 Description 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: ...

最新文章

  1. ASP.NET MVC动作过滤器
  2. 【LInux】查看Linux系统版本信息
  3. 小程序input框letter-spacing失效,处理方法
  4. 数组各元素出现的次数
  5. Springboot后台接收前端Date类型
  6. OpenStack精华问答 | NOVA计算服务
  7. 幸福框架:模式驱动开发
  8. jquery利用appendTo动态创建元素
  9. asp.net C#实现下载文件的六种方法实例
  10. arcgis10之将多个shp文件合并成一个shp文件
  11. 基于DFS的拓扑排序算法实现
  12. 银联支付接口申请开发教程-傲付宝
  13. 行业大数据技术发展趋势
  14. 如何永久性取消WPS热点推广
  15. Ubuntu16.04 安装LSD-SLAM
  16. jzoj5442. 【NOIP2017提高A组冲刺11.1】荒诞
  17. 多维尺度分析(Multidimensional scaling,MDS)及SPSS实现
  18. python对财务的作用_学习Python对财务工作者有哪些用途?
  19. python获取邮件并转为pdf
  20. softer nms论文阅读Bounding Box Regression with Uncertainty for Accurate Object Detection

热门文章

  1. tplink打印机服务器重置,tl-wdr3310路由器怎么设置打印机服务器
  2. 极光推送REST API
  3. 程序员去哪里找兼职?哪里可以接程序员私活?
  4. everyday misson 情态动词
  5. 【Servlet】上传文件时,ServletFileUpload.isMultipartContent(req)结果一直为false
  6. js-cookie 设置过期时间
  7. 将个人java web网站发布至公网#内网穿透#花生壳#手把手教程
  8. ASP导入Excel数据提示:外部数据库驱动程序(1)中的意外错误 解决办法
  9. iOS 中使用 Mobile Installation 安装 IPA-获取列表(使用iTunes/AppStore一样的安装API)
  10. 原创分析:iOS 中使用 Mobile Installation 安装 IPA(使用iTunes/AppStore一样的安装API)