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. android 学习六 构建用户界面和使用控件

    1.常用Android控件最终都会继承自View类 2.ViewGroup是一些布局类列表的基类,包括View和ViewGroup 3.构造界面的三种方法    a.完全使用代码(太灵活,而不好维护) ...

  2. 「题目代码」P1039~P1043(Java)

    P1039 谭浩强C语言(第三版)习题4.9 import java.util.*; import java.io.*; import java.math.BigInteger; public cla ...

  3. 记录---Testin上新手测试用例设计实战---碎乐3.2.0

    平台上给的版本是碎乐3.12版的,但是平台上给的安装包下载不了,所以加群咨询之后给出了直接去手机应用商店下载搜索到的版本的对策.所以就那应用商店中找到的3.2.0版本来设计测试用例.因为任务中没有给出 ...

  4. Jquery获取DOM绑定事件

    获取到当前正在执行的事件: $('#testDive').bind('click', function(event){alert('event: ' + event.type)}); 获取所有绑定事件 ...

  5. js 加密 crypto-js des加密

    js 加密 crypto-js    https://www.npmjs.com/package/crypto-js   DES  举例:   js 引入:   <script src=&quo ...

  6. python leveldb 文档

    标签(空格分隔): python leveldb import leveldb db = leveldb.LevelDB('./db') db.Put('hello', 'world') print ...

  7. Hadoop源码解析 1 --- Hadoop工程包架构解析

    1 Hadoop中各工程包依赖简述     Google的核心竞争技术是它的计算平台.Google的大牛们用了下面5篇文章,介绍了它们的计算设施.     GoogleCluster: http:// ...

  8. Redis+Keepalived高可用方案详细分析

    背景 目前,Redis集群的官方方案还处在开发测试中,未集成到稳定版中.且目前官方开发中的Redis Cluster提供的功能尚不完善(可参考官方网站或http://www.redisdoc.com/ ...

  9. 上层应用与wpa_supplicant,wpa_supplicant与kernel 相关socket创建交互分析

    单独拿出来,分析以下上层应用与wpa_supplicant   wpa_supplicant与kernel 的socket交互. 关联上层应用与wpa_supplicant的socket的创建.连接流 ...

  10. Python中的Comprehensions和Generations

    Python中的Comprehensions和Generations语法都是用来迭代的.Comprehensions语法可用于list,set,dictionary上,而Generations语法分为 ...