bzoj

luogu

Description

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

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

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

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

Input

第一行给出一个整数n, 表示密室的个数,

第二行至第 n 行, 每行绐出两个整数 a 和 b, 表示第a个密室和第b个密室之间存在一条通道。

第 n+1行给出 n 个整数,分别表示当时每个密室的启动情况 (0表示关闭, 1表示启动)。

第 n+2行给出 n 个整数, 分别表示密码中每个密室的启动情况。

Output

输出只有一行,即输出最少改动标记次数

Sample Input

4

1 2

2 3

3 4

0 0 1 1

1 0 0 0

Sample Output

1

Hint

密室的编号是可以变的!将第三个密室关闭后,在当前标记和密码之间,存在1->4,2->3,3->2,4->1 的对应关系,重新编号后连接情况没有改变,且标记与密码对应。对于更一般的情况,存在一个1 到n 的置换P,使得对于任意密室之间的道路u-v,都一定有密码密室中的道路P(u)-P(v);如果不存在密室之间的道路u-v,则一定没有密码密室中的道路P(u)-P(v)。

对于100%的数据,n<=700,且每个密室至多与11个密室相通

sol

首先判断树同构可以使用树\(hash\)的方法,为了避免出错可以把很多东西\(hash\)进去,比如说子树\(size\),儿子个数等等。

然后就是转移了。我们设\(f_{i,j}\)表示\(i\)号点去与\(j\)号点对应匹配的最小代价,这里要求\(i,j\)同构,那么\(i,j\)的各个儿子也应该会分别对应同构。我们现在最关键的问题就是处理\(i,j\)的各个儿子的匹配关系,使得总代价最小。

我们按照深度由大到小处理,这样在处理\(i,j\)时,它们的儿子的答案就已经算好了。在双方同构的儿子之间连权值为答案的边,这样就成了二分图最小权匹配,直接使用费用流即可。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
#define ull unsigned long long
const int N = 1005;
int n,col1[N],col2[N],id[N],f[N][N];
struct MCMF{
struct edge{int to,nxt,w,cost;}a[N*N];
int S,T,head[N],cnt,dis[N],vis[N],pe[N],ans;
queue<int>Q;
void init(){memset(head,0,sizeof(head));cnt=1;}
void link(int u,int v,int w,int cost){
a[++cnt]=(edge){v,head[u],w,cost};head[u]=cnt;
a[++cnt]=(edge){u,head[v],0,-cost};head[v]=cnt;
}
bool spfa(){
memset(dis,63,sizeof(dis));
dis[S]=0;Q.push(S);
while (!Q.empty()){
int u=Q.front();Q.pop();
for (int e=head[u];e;e=a[e].nxt){
int v=a[e].to;
if (a[e].w&&dis[v]>dis[u]+a[e].cost){
dis[v]=dis[u]+a[e].cost;pe[v]=e;
if (!vis[v]) vis[v]=1,Q.push(v);
}
}
vis[u]=0;
}
if (dis[T]==dis[0]) return false;
ans+=dis[T];
for (int i=T;i!=S;i=a[pe[i]^1].to)
--a[pe[i]].w,++a[pe[i]^1].w;
return true;
}
int work(){ans=0;while(spfa());return ans;}
}G;
const ull base1 = 20020415;
const ull base2 = 20011118;
struct TREE{
int to[N<<1],nxt[N<<1],head[N<<1],cnt;
int root,sz[N],w[N],fa[N],dep[N];ull Hash[N];
void link(int u,int v){
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void getroot(int u,int f){
sz[u]=1;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f){
getroot(to[e],u);
sz[u]+=sz[to[e]];
w[u]=max(w[u],sz[to[e]]);
}
w[u]=max(w[u],n-sz[u]);
if (w[u]<w[root]) root=u;
}
ull tmp[N];
void dfs(int u,int f){
fa[u]=f;dep[u]=dep[f]+1;sz[u]=1;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f) dfs(to[e],u),sz[u]+=sz[to[e]];
int len=0;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f) tmp[++len]=Hash[to[e]];
sort(tmp+1,tmp+len+1);
Hash[u]=(base1*len)^sz[u];
for (int i=1;i<=len;++i) Hash[u]=((Hash[u]*base2)^tmp[i])+tmp[i];
}
void work(){
w[0]=n;
getroot(1,0);getroot(root,0);
for (int e=head[root],lst=0;e;lst=e,e=nxt[e])
if (sz[to[e]]*2==n){
++n;
if (e==head[root]) head[root]=nxt[e];
else nxt[lst]=nxt[e];
for (int i=head[to[e]],Lst=0;i;Lst=i,i=nxt[i])
if (to[i]==root){
if (i==head[to[e]]) head[to[e]]=nxt[i];
else nxt[Lst]=nxt[i];
break;
}
link(n,root);link(root,n);link(n,to[e]);link(to[e],n);
root=n;break;
}
dfs(root,0);
}
int cal(int u,int v){
int s1[N],s2[N],t1=0,t2=0;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=fa[u]) s1[++t1]=to[e];
for (int e=head[v];e;e=nxt[e])
if (to[e]!=fa[v]) s2[++t2]=to[e];
G.init();G.S=t1+t2+1;G.T=G.S+1;
for (int i=1;i<=t1;++i) G.link(G.S,i,1,0);
for (int i=1;i<=t2;++i) G.link(i+t1,G.T,1,0);
for (int i=1;i<=t1;++i)
for (int j=1;j<=t2;++j)
if (Hash[s1[i]]==Hash[s2[j]])
G.link(i,j+t1,1,f[s1[i]][s2[j]]);
return G.work()+(col1[u]!=col2[v]);
}
}T;
bool cmp(int i,int j){
return T.dep[i]==T.dep[j]?T.Hash[i]<T.Hash[j]:T.dep[i]>T.dep[j];
}
int main(){
n=gi();
for (int i=1;i<n;++i){
int u=gi(),v=gi();
T.link(u,v);T.link(v,u);
}
for (int i=1;i<=n;++i) col1[i]=gi();
for (int i=1;i<=n;++i) col2[i]=gi();
T.work();
for (int i=1;i<=n;++i) id[i]=i;
sort(id+1,id+n+1,cmp);
for (int i=1,j;i<=n;i=j){
j=i+1;
while (j<=n&&T.dep[id[i]]==T.dep[id[j]]&&T.Hash[id[i]]==T.Hash[id[j]]) ++j;
for (int k=i;k<j;++k)
for (int l=i;l<j;++l)
f[id[k]][id[l]]=T.cal(id[k],id[l]);
}
printf("%d\n",f[T.root][T.root]);return 0;
}

[BZOJ3197][SDOI2013]刺客信条assassin的更多相关文章

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

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

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

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

  3. Bzoj3197: [Sdoi2013]assassin

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

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

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

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

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

  6. [SDOI2013]刺客信条

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

  7. JZOJ 3296 Luogu P3296 [SDOI2013]刺客信条

    前言 做法来自:@pzrpzr ,写一下!Orz pzr! 题目大意 \(n\) 个点的无根树,每个点有两个 \(0/1\) 权值,合适地安排节点在同构树中的顺序,使得前后对应的权值不同节点个数最小, ...

  8. 【BZOJ3197】[Sdoi2013]assassin 树同构+动态规划+KM

    [BZOJ3197][Sdoi2013]assassin Description Input Output Sample Input 4 1 2 2 3 3 4 0 0 1 1 1 0 0 0 Sam ...

  9. bzoj 3197 [Sdoi2013]assassin(Hash+DP+KM)

    Description Input Output Sample Input 4 1 2 2 3 3 4 0 0 1 1 1 0 0 0 Sample Output 1 HINT [思路] Hash,D ...

随机推荐

  1. 使用 Vs 2015 快速上手 Angular2

    Visual Studio 2015 快速上手(使用Angular2)https://angular.cn/guide/visual-studio-2015 使用 Vs 2015 快速上手 Angul ...

  2. 创建自定义JSR303的验证约束(Creating custom constraints)

    转载:http://clongjava.iteye.com/blog/1317649 由于输入验证在软件开发中是必须的一件事情,特别是与用户交互的软件产品,验证用户的潜在输入错误是必不可少的一件事情, ...

  3. css居中方法详解

    水平居中: 通过设置父元素,让子元素内容居中:text-align:center; 通过设置子元素本身,让子元素居中:margin:0 auto; 以上方法生效的前提条件是子元素没有被float元素影 ...

  4. Spring入门2. IoC中装配Bean

    Spring入门2. IoC中装配Bean 20131125 前言: 上一节学习了Spring在JavaProject中的配置,通过配置文件利用BeanFactory和ApplicationConte ...

  5. linux中的/usr,/var,/opt目录详解

    转自:http://it.greenblogs.org/archives/2008/20113.shtml/ /usr文件系统  /usr 文件系统经常很大,因为所有程序安装在这里. /usr 里的所 ...

  6. iOS 可能用到的三方框架

    1.MWPhotoBrowser 第三方图片浏览器 https://github.com/mwaterfall/MWPhotoBrowser 2.SlackTextViewController  强大 ...

  7. Flask框架中特有的变量/函数及上下文

    模板中特有的变量和函数 你可以在自己的模板中访问一些 Flask 默认内置的函数和对象 config 你可以从模板中直接访问Flask当前的config对象: {{config.SQLALCHEMY_ ...

  8. 手机微信网站开发放弃windows phone的理由

    1.手机操作系统市场份额: 全球(2.44% 下滑): http://news.mydrivers.com/1/449/449736.htm 中国(1% 下滑): http://app.techweb ...

  9. css hover伪类选择器与JQuery hover()方法

    css hover伪类选择器 它属于anchor伪类 在支持 CSS 的浏览器中,<a>标签链接的不同状态都可以以不同的方式显示,常常用来改链接的颜色效果 实例 a:link {color ...

  10. Lodash 浓缩

    Lodash 是个十分有用的工具库,但我们往往只需要其中的一小部分函数.这时,整个 lodash 库就显得十分庞大,我们需要减小 lodash 的体积. cherry-pick 方法 Lodash 官 ...