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. DSP5509的RTC实验-第3篇

    1. RTC实时时钟,不在过多介绍,本例程直接调用芯片支持库CSL的库函数,用起来比较简单 main() { CSL_init(); printf ("\nTESTING...\n" ...

  2. ORA-15032、ORA-15033—Linux环境

    SQL> alter diskgroup DATA add failgroup DATA_0000 disk '/dev/raw/raw12'; alter diskgroup DATA add ...

  3. web漏洞原理 (需要每周更新此篇文章)

    SQL注入攻击简介 结构化查询语言SQL是用来和关系数据库进行交互的文本语言.它允许用户对数据进行有效的管理,包含了对数据的查询.操作.定义和控制等几个方面,例如向数据库写入.插入数据,从数据库读取数 ...

  4. Ubuntu卡在logo界面

    对于这个问题,我也是在最近一次偶然的机会中发现的. 我重装了了Ubuntu 18.04, 很多东西需要重新配置,  有个刚性需求就是配置shadowsocks实现***,对于从windows向linu ...

  5. spark dataset join 使用方法java

    dataset<Row> df1,df2,df3 //该方法可以执行成功 df3= df1.join(df2,"post_id").selectExpr("h ...

  6. 聊聊、dubbo 找不到 dubbo.xsd 报错

    平常在用 Dubbo 的时候,创建 xml 会提示 http://code.alibabatech.com/schema/dubbo/dubbo.xsd 找不到. 大家可以去 https://gith ...

  7. 为什么请求时,需要使用URLEncode做encode转码操作(转)

    什么要对url进行encode 发现现在几乎所有的网站都对url中的汉字和特殊的字符,进行了urlencode操作,也就是: http://hi.baidu.com/%BE%B2%D0%C4%C0%C ...

  8. Caused by: java.lang.NoClassDefFoundError: javax/el/ELManager

    出现问题的原因: 在将springboot项目部署到Linux下的Tomcat中,项目无法正常启动(本地能正常运行),Tomcat启动日志中出现: Caused by: java.lang.NoCla ...

  9. JavaScript筑基篇(三)->JS原型和原型链的理解

    删除理由:很久以前写的,当时理解不够深入,这样描述反而看起来更复杂了.因此就删掉,免得误人子弟! 可以看看另一篇文章:[如何继承Date对象?由一道题彻底弄懂JS继承.](http://www.cnb ...

  10. defineporperty 的使用 设置对象的只读或只写属性

    <!DOCTYPE html> <html lang="en"> <head> <title>Document</title& ...