前言

做法来自:@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. IDEA 使用rest client测试

    一.进入 rest 控制台 idea 导航栏 ==> Tools ==> HTTP Client ==> Test RESTFUL Web Service 如图: 一般来说,idea ...

  2. 1.Thmeleaf模板引擎

    1.Thmeleaf的基本语法 大部分的Thmeleaf表达式都直接被设置到HTML节点中,作为HTML节点的一个属性存在,这样同一个模板文件既可以使用浏览器直接打开,也可以放到服务器中用来显示数据, ...

  3. java 整型

    byte(1字节).short(2字节).int(4字节).long(16字节) java中前缀加上0b或者0B就可以写二进制数,前缀加上0就可以写八进制数,前缀加上0x或者0X就可以写十六进制数 一 ...

  4. Airtest结合tidevice实现IOS自动化测试

    这篇博文内容,是基于之前的配置而来的.我们可以先回顾一下之前博文,Windows搭建mac黑苹果系统:WebDriverAgent重签名爬坑记 . 今天来分享下如何通过 tidevice实现IOS自动 ...

  5. 我的邮箱客户端程序Popmail

    05年的时候写了一个邮箱客户端程序.当时主要目的是研究POP3和SMTP协议,同时锻炼自己的网络编程能力.当然了,如果自己写的邮箱客户端能够满足自身的日常工作需要,而不是频繁的登录不同的网页邮箱,那就 ...

  6. Data truncation: Truncated incorrect DOUBLE value错误的解决方案

    Data truncation: Truncated incorrect DOUBLE value错误的解决方案: 当在修改某条单位记录时,发生了Data truncation: Truncated ...

  7. 缓存Bigkey坚决不要用,拆分是王道

    大家好,我是架构摆渡人.这是实践经验系列的第四篇文章,这个系列会给大家分享很多在实际工作中有用的经验,如果有收获,还请分享给更多的朋友. 背景介绍 在高并发的业务场景中,缓存是必须要上的,用来扛高并发 ...

  8. cmake之Visual studio无法显示头文件

    本文演示cmake版本:3.18 1. 问题 使用cmake创建的Visual Studio 项目都没有显示头文件, 比如: 可以清楚的看见,项目lib_pipe没有显示头文件 2. 配置CMakeL ...

  9. Android 控件使用教程(二)—— RecyclerView 展示图片

    简介 在上一篇博文中,介绍了大家已经很熟悉的布局控件ListView,在这篇文章中,我将使用比较新.功能也更强大的RecyclerView. RecyclerView 首先,要用这个控件,你需要在gr ...

  10. 【LeetCode】723. Candy Crush 解题报告 (C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 暴力 日期 题目地址:https://leetcode ...