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=;
const int N=;
const int B=;
const int INF=1e9;
inline int read(){
int X=,w=;char ch=;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch))X=(X<<)+(X<<)+(ch^),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]=;
for(int v=;v<=n;v++){
if(!vy[v]){
int w=wx[u]+wy[v]-dis[u][v];
if(!w){
vy[v]=;
if(!match[v]||dfs2(match[v],n)){
match[v]=u;return ;
}
}else sla[v]=min(sla[v],w);
}
}
return ;
}
int KM(int n){
memset(wx,-,sizeof(wx));
memset(wy,-,sizeof(wy));
memset(match,,sizeof(match));
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
wx[i]=max(wx[i],dis[i][j]);
for(int i=;i<=n;i++){
memset(sla,,sizeof(sla));
while(){
memset(vx,,sizeof(vx));
memset(vy,,sizeof(vy));
if(dfs2(i,n))break;
int minn=INF;
for(int j=;j<=n;j++)
if(!vy[j])minn=min(minn,sla[j]);
for(int j=;j<=n;j++){
if(vx[j])wx[j]-=minn;
if(vy[j])wy[j]+=minn;
else sla[j]-=minn;
}
}
}
int ans=;
for(int i=;i<=n;i++)if(dis[match[i]][i]!=-INF)ans-=dis[match[i]][i];
return ans;
}
struct node{
int to,nxt;
}e[N*];
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=,g1,g2=,maxn=n;
q[++r]=st;fa[st]=;
for(int l=;l<=r;l++){
int u=q[l];size[u]=;son[u]=;
for(int i=head[u];i!=-;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>=;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=;
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!=-;i=e[i].nxt){
int v=e[i].to;
if(v==g2){
e[i].to=++n;e[i^].to=n;
add(n,g1);add(n,g2);
return n;
}
}
}
ll dfs1(int u){
ll num[N];int r=;
size[u]=;h[u]=B;
for(int i=head[u];i!=-;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u])continue;
fa[v]=u;dep[v]=dep[u]+;num[++r]=dfs1(v);size[u]+=size[v];
}
sort(num+,num+r+);
for(int i=;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=,idx2=;
memset(to1,,sizeof(to1));
memset(to2,,sizeof(to2));
for(int i=head[u1];i!=-;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!=-;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=;
q[++r]=s;
for(int l=;l<=r;l++){
int u=q[l];
for(int i=head[u];i!=-;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u])continue;
q[++r]=v;
}
}
for(int i=r;i>=;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-;
}
}
return find(s,s);
}
int main(){
cnt=-,memset(head,-,sizeof(head));
n=read();
for(int i=;i<n;i++){
int u=read(),v=read();
add(u,v);add(v,u);
}
for(int i=;i<=n;i++)a[i]=read();
for(int i=;i<=n;i++)b[i]=read();
int rt=calcg();fa[rt]=;
dfs1(rt);
for(int i=;i<=n;i++)for(int j=;j<=n;j++)f[i][j]=INF;
printf("%d\n",bfs(rt));
return ;
}

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

+本文作者:luyouqi233。               +

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

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

BZOJ3197:[SDOI2013]刺客信条——题解的更多相关文章

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

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

  2. 【BZOJ3197】[SDOI2013]刺客信条

    [BZOJ3197][SDOI2013]刺客信条 题面 bzoj 洛谷 题解 关于树的同构,有一个非常好的性质: 把树的重心抠出来,那么会出现两种情况: 1.有一个重心,那么我们直接把这个重心作为树的 ...

  3. Bzoj3197/洛谷3296 [SDOI2013]刺客信条assassin(树的重心+树Hash+树形DP+KM)

    题面 Bzoj 洛谷 题解 (除了代码均摘自喻队的博客,可是他退役了) 首先固定一棵树,枚举另一棵树,显然另一棵树只有与这棵树同构才有可能产生贡献 如果固定的树以重心为根,那么另一棵树最多就只有重心为 ...

  4. Bzoj 3124: [Sdoi2013]直径 题解

    3124: [Sdoi2013]直径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1222  Solved: 580[Submit][Status] ...

  5. Bzoj 3131 [Sdoi2013]淘金 题解

    3131: [Sdoi2013]淘金 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 733  Solved: 363[Submit][Status][ ...

  6. Bzoj3197: [Sdoi2013]assassin

    题面 传送门 Sol 套路:找出重心,如果有两个就新建一个点 然后把这棵树hash一下 设\(f[i][j]\)表示第一颗树到\(i\)第二棵树到\(j\),子树\(i,j\)同构的付出的最小代价 转 ...

  7. BZOJ3123:[SDOI2013]森林——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=3123 https://www.luogu.org/problemnew/show/P3302 树上主 ...

  8. [SDOI2013]刺客信条

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

  9. [SDOI2013]直径 题解

    题面 这道题明显的一定要找到直径的具体路径,所以两遍dfs是比较好的选择: 第一问是一道弱智题吧? 主要难度全部分摊在了第二问: 其实不难,先找到任意一个直径: 对于任意一个在直径上的点: 设nxt[ ...

随机推荐

  1. MyBatis-SpringMVC整合

    1.添加spring相关jar包 2.配置ehcache jar包. 3.添加ehcache mybatis 适配器jar包(在mybatis官网) 4.添加spring mybatis 适配器jar ...

  2. SpringCloud Eureka 服务注册与服务发现

    一.Eureka简介 spring Cloud Netflix技术栈中,Eureka作为服务注册中心对整个微服务架构起着最核心的整合作用.有了服务发现与注册,你就不需要整天改服务调用的配置文件了,你只 ...

  3. 「日常训练」Maximum Multiple(HDU-6298)

    题意与分析 一开始以为是一条高深的数学题,跳过去了,后来查其他题目的代码的时候无意看到,一看emmmmmm 稍微思考一下就有了.\(1=\frac{1}{3}+\frac{1}{3}+\frac{1} ...

  4. Java 递归 反射 正则表达式

    一 递归 1. 就是函数自身调用自身 (就是在栈内存中不断的加载同一个函数) 2. 什么时候用递归呢? 当一个功能被重复使用 而每一次使用该功能时的参数不确定 都由上次的功能元素结果来确定 简单说: ...

  5. 孤荷凌寒自学python第八十三天初次接触ocr配置tesseract环境

    孤荷凌寒自学python第八十三天初次接触ocr配置tesseract环境 (完整学习过程屏幕记录视频地址在文末) 学习Python我肯定不会错过图片文字的识别,当然更重要的是简单的验证码识别了,今天 ...

  6. Git 简易食用指南 v2.0

    写在前面 一开始我们先聊一聊版本控制,什么是版本控制呢?版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统.具体大类分为: 本地版本控制系统 集中式版本控制系统SVN 分布式 ...

  7. 【MySQL解惑笔记】Navicat 无法远程连接MySQL数据库

    安装好Navicat之后远程连接MySQL数据库出现以下报错截图: 出现以上截图怀疑是mysql用户权限不够: GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.1 ...

  8. ElasticSearch 论坛搜索查询语句

    概述 研究论坛搜索如何综合时间和TF/IDF权重. 自定义权重计算的效率问题 数据结构 假设有一个论坛的搜索 字段包括: subject:标题 message:内容 dateline:发布时间 tag ...

  9. 常用算法Java实现之快速排序

    快速排序和冒泡排序相似,都是通过多次比较和交换来实现排序. 具体流程如下: 1.首先设定一个分界值,通过分界值将数组分成左右两部分,将大于等于分界值的数据交换集中到右侧数组,将小于分界值的数据交换集中 ...

  10. 对Android体系结构的理解--后续会补充

    1.最底层_硬件 任何Android设备最底层的硬件包括 显示屏, wifi ,存储设备 等. Android最底层的硬件会根据需要进行裁剪,选择自己需要的硬件. 2.Linux内核层 该层主要对硬件 ...