前言

做法来自:@pzrpzr ,写一下!Orz pzr!

题目大意

\(n\) 个点的无根树,每个点有两个 \(0/1\) 权值,合适地安排节点在同构树中的顺序,使得前后对应的权值不同节点个数最小,并输出。

解题思路

首先,我们套路地将无根树的同构问题转化成以重心为根的有根树的同构问题。(相关证明请查阅相关资料,此处不赘述。)

找出重心,以此为根。若有两个重心,则新增一个点,使其为根,并删去原重心之间的连边,且两个重心分别向根连边。不难发现此节点一定为重心,且不影响答案。

注意到一个条件即一个节点的度数 \(<=11\),这启示我们使用dp解决这个问题。( \(son_x\) 表示 \(x\) 的儿子个数。)

设 \(f_{x,y}\) 表示 \(x\) 子树与 \(y\) 子树同构,\(x\) 对应 \(y\) 时,最小的代价。容易发现 \(x\) 和 \(y\) 的深度、儿子个数、子树大小均应该相等。故真正有用的状态很小。

至于转移,pzr想到不需要通过树hash进行判断同构。如果我们能将 \(x\) 和 \(y\) 的子树分别一一配对,由其转移即可。设一个辅助dp数组 \(g_{i,s}\) 表示 \(x\) 的儿子匹配到第 \(i\) 个,\(y\) 的儿子匹配的状态为 \(s\) 的最小代价。转移可以枚举子集。显然

\[f_{x,y}=g_{son_x,2^{son_y+1}-1}(son_x=son_y)+[val_{1,i} \ne val_{2,j}]
\]

时间复杂度不好分析,恳请大神帮忙算一下。

最后要注意,找到重心以后 \(siz\) , \(son\) 要重新算。因为这个wa了一发。

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define M 13
#define W 1030
#define N 710
#define INF 0x3f3f3f3f
#define ff(i, a, b) for(int i = (a); i < (b); ++i)
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
inline int read() // negative , long long
{
int x = 0; char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x;
}
int n, p2[M] = {1}, cnt[W], siz[N], fa[N], son[N], c[3], f[N][N], g[M][W];
int last[N], pre[N << 1], to[N << 1];
bool op1[N], op2[N];
inline void add(int u, int v){static int tot = 0; pre[++tot] = last[u], to[tot] = v, last[u] = tot;}
void findC(int u)
{
siz[u] = 1, son[u] = 0;
bool ok = 1;
for(int i = last[u]; i; i = pre[i])
{
int v = to[i];
if(v == fa[u]) continue ;
fa[v] = u, findC(v), ++son[u], siz[u] += siz[v];
if(siz[v] > (n >> 1)) ok = 0;
}
if(ok && n - siz[u] <= (n >> 1)) c[++c[0]] = u;
}
void delEdge(int u, int v)
{
if(to[last[u]] ^ v)
for(int i = last[u]; pre[i]; i = pre[i])
if(to[pre[i]] == v && (pre[i] = pre[pre[i]])) return ;
last[u] = pre[last[u]];
}
void dfs(int x, int y)
{
f[x][y] = INF;
vector<int> sx, sy;
int full = p2[son[y]] - 1, s = son[x];
if(s != son[y] || siz[x] != siz[y]) return ;
for(int i = last[x]; i; i = pre[i])
if(to[i] ^ fa[x]) sx.push_back(to[i]);
for(int i = last[y]; i; i = pre[i])
if(to[i] ^ fa[y]) sy.push_back(to[i]);
fo(i, 1, s) fo(j, 1, s)
dfs(sx[i - 1], sy[j - 1]);
fo(i, 1, s) memset(g[i], 0x3f, sizeof(int) * (full + 3));
fo(i, 1, s) fo(j, 1, s) fo(S, p2[j - 1], full)
if(cnt[S] == i && (S & p2[j - 1]))
g[i][S] = min(g[i][S], g[i - 1][S ^ p2[j - 1]] + f[sx[i - 1]][sy[j - 1]]);
f[x][y] = min(f[x][y], g[s][full] + (op1[x] != op2[y]));
}
int main()
{
n = read();
fo(i, 1, 10) p2[i] = p2[i - 1] << 1;
fo(i, 1, p2[10]) cnt[i] = cnt[i >> 1] + (i & 1);
int u, v, rt;
fo(i, 2, n) u = read(), v = read(), add(u, v), add(v, u);
fo(i, 1, n) op1[i] = read();
fo(i, 1, n) op2[i] = read();
findC(1);
if(c[0] ^ 1)
{
rt = ++n; add(n, c[1]), add(n, c[2]), add(c[1], n), add(c[2], n);
delEdge(c[1], c[2]), delEdge(c[2], c[1]);
}
else rt = c[1];
fa[rt] = 0, findC(rt);
dfs(rt, rt);
printf("%d\n", f[rt][rt]);
return 0;
}

JZOJ 3296 Luogu P3296 [SDOI2013]刺客信条的更多相关文章

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

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

  2. luogu P3305 [SDOI2013]费用流

    题目链接 bz似乎挂了... luogu P3305 [SDOI2013]费用流 题解 dalao告诉我,这题 似乎很水.... 懂了题目大意就可以随便切了 问1,最大流 问2,二分最大边权求,che ...

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

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

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

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

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

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

  6. BZOJ 3203 Luogu P3299 [SDOI2013]保护出题人 (凸包、斜率优化、二分)

    惊了,我怎么这么菜啊.. 题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=3203 (luogu)https://www.lu ...

  7. Luogu P3305 [SDOI2013]费用流 二分 网络流

    题目链接 \(Click\) \(Here\) 非常有趣的一个题目. 关键结论:所有的单位费用应该被分配在流量最大的边上. 即:在保证最大流的前提下,使最大流量最小.这里我们采用二分的方法,每次判断让 ...

  8. luogu P3297 [SDOI2013]逃考

    传送门 gugugu 首先每个人管理的区域是一个多边形,并且整个矩形是被这样的多边形填满的.现在的问题是求一条经过多边形最少的路径到达边界,这个可以最短路. 现在的问题是建图,显然我们应该给相邻的多边 ...

  9. luogu P3304 [SDOI2013]直径

    树的直径两遍dfs救星了 至于一定在直径中的边数,可以发现这些边一定是连续的(不然你两条直径中间能有空挡?),然后,如果某个点往下有多条直径,那么这条点以下都不算入答案.所以以直径分别两端点为根,找出 ...

随机推荐

  1. MyBatis(2):CRUD操作

    编写接口 import com.shandx.pojo.User; import java.util.List; public interface UserMapper { <span clas ...

  2. 【Linux】【Services】【SaaS】Docker+kubernetes(7. 安装Docker私有镜像仓库)

    1. 简介 1.1. 自己做个私有镜像,方便上传和下载,我也在docker官网注册了一个账号,做好的镜像可以传上去 1.2. Redhat自带私有镜像的功能,需要安装包,这是howto: https: ...

  3. 快速上手ANTLR

    回顾前文: ANTLR 简单介绍 ANTLR 相关术语 ANTLR 环境准备 下面通过两个实例来快速上手ANTLR. 使用Listener转换数组 完整源码见:https://github.com/b ...

  4. 云服务器ECS部署和卸载RabbitMQ

    云服务器ECS部署RabbitMQ RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件,用于在分布式系统中存储转发消息,有良好的易用性.扩展性和高可用性.本文介绍如何通过ECS实例 ...

  5. ERROR: Command errored out with exit status 1:安装pip3 install --user pyecharts==0.5.11失败问题总结

    一.前言:最近在学习pyecharts学习到Grid时候发现代码无法运行了,经过在网上查找资料说是pyecharts版本不适配了,之前的版本是 pip install pyecharts==0.1.9 ...

  6. .Net Core服务诊断排查

    前言: 近期在项目中出现了几次服务内存资源占用较高的情况,特回顾梳理下排查过程以及对相应问题的排查方法总结. 一.Dump抓取 抓取dump的方式有多种,下面介绍几种常用的: 1. 任务管理器中找到程 ...

  7. CF748A Santa Claus and a Place in a Class 题解

    Content 圣诞老人坐在一个桌子被摆成 \(m\) 行 \(n\) 列的教室里.每个桌子有两个座位,每个位置从左往右都有编号,依次为 \(1,2,3,...,2\times n\times m\) ...

  8. Open-UI序言

    Open_UI-----序言 在单片机开发的时候,我们时常需要一个足够开放的,体积足够小巧,且移植性较高的UI库来显示信息,从而做到非常优秀的人机交互效果.而我试过的UI库如下: LVGL ​ 这是目 ...

  9. JAVA获取某个月(当月)第一天的开始时刻和某个月(当月)最后一天的最后时刻

    package com.date; import java.text.SimpleDateFormat; import java.util.Calendar; public class Test { ...

  10. CMake判断操作系统和编译器

    判断操作系统 IF (CMAKE_SYSTEM_NAME MATCHES "Linux") ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Wind ...