JZOJ 3296 Luogu P3296 [SDOI2013]刺客信条
前言
做法来自:@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\) 的最小代价。转移可以枚举子集。显然
\]
。
时间复杂度不好分析,恳请大神帮忙算一下。
最后要注意,找到重心以后 \(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]刺客信条的更多相关文章
- 【BZOJ3197】[SDOI2013]刺客信条
[BZOJ3197][SDOI2013]刺客信条 题面 bzoj 洛谷 题解 关于树的同构,有一个非常好的性质: 把树的重心抠出来,那么会出现两种情况: 1.有一个重心,那么我们直接把这个重心作为树的 ...
- luogu P3305 [SDOI2013]费用流
题目链接 bz似乎挂了... luogu P3305 [SDOI2013]费用流 题解 dalao告诉我,这题 似乎很水.... 懂了题目大意就可以随便切了 问1,最大流 问2,二分最大边权求,che ...
- Bzoj3197/洛谷3296 [SDOI2013]刺客信条assassin(树的重心+树Hash+树形DP+KM)
题面 Bzoj 洛谷 题解 (除了代码均摘自喻队的博客,可是他退役了) 首先固定一棵树,枚举另一棵树,显然另一棵树只有与这棵树同构才有可能产生贡献 如果固定的树以重心为根,那么另一棵树最多就只有重心为 ...
- [BZOJ3197][SDOI2013]刺客信条assassin
bzoj luogu Description 故事发生在1486 年的意大利,Ezio原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的 ...
- BZOJ3197:[SDOI2013]刺客信条——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3197 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受 ...
- BZOJ 3203 Luogu P3299 [SDOI2013]保护出题人 (凸包、斜率优化、二分)
惊了,我怎么这么菜啊.. 题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=3203 (luogu)https://www.lu ...
- Luogu P3305 [SDOI2013]费用流 二分 网络流
题目链接 \(Click\) \(Here\) 非常有趣的一个题目. 关键结论:所有的单位费用应该被分配在流量最大的边上. 即:在保证最大流的前提下,使最大流量最小.这里我们采用二分的方法,每次判断让 ...
- luogu P3297 [SDOI2013]逃考
传送门 gugugu 首先每个人管理的区域是一个多边形,并且整个矩形是被这样的多边形填满的.现在的问题是求一条经过多边形最少的路径到达边界,这个可以最短路. 现在的问题是建图,显然我们应该给相邻的多边 ...
- luogu P3304 [SDOI2013]直径
树的直径两遍dfs救星了 至于一定在直径中的边数,可以发现这些边一定是连续的(不然你两条直径中间能有空挡?),然后,如果某个点往下有多条直径,那么这条点以下都不算入答案.所以以直径分别两端点为根,找出 ...
随机推荐
- Java Bean 与Spring Bean 的区别
什么是JavaBean: JavaBean是一种JAVA语言写的可重用组件.JavaBean符合一定规范写的Java类,是一种规范.它的方法命名,构造以及行为必须符合特定的要求: 1.所有属性 ...
- Java 设计模式--策略模式,枚举+工厂方法实现
如果项目中的一个页面跳转功能存在10个以上的if else判断,想要做一下整改 一.什么是策略模式 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决 ...
- 粒子群算法-PSO
粒子群优化算法 1. 背景知识 1995年美国社会心理学家Kennedy和电气工程师Eberhart共同提出粒子群优化算法(Particle Swarm Optimization, PSO).PSO算 ...
- 资源分配(Project)
<Project2016 企业项目管理实践>张会斌 董方好 编著 资源设置好以后,不能光摆着看,分配到各任务中去才是正道. 分配资源就需要回到与任务相关的视图了,比如[任务工作表]视图或者 ...
- Vlookup大叔早已油腻(Excel函数集团)
1.Vlookup基础用法:https://mp.weixin.qq.com/s/0JRytWlBD-KYM2jkecG2rA 2.合并字符串:https://mp.weixin.qq.com/s/9 ...
- CF1557B Moamen and k-subarrays 题解
Content 给定一个大小为 \(n\) 的数组.你可以将其分为 \(k\) 个子数组,并按照每个子数组的字典序重新排列这些子数组,再顺次拼接,得到一个新的数组.问是否存在一种划分子数组的方案,使得 ...
- Python+Robot Framework实现UDS诊断自动化测试
一.环境搭建 1.概述 由于项目需要进行UDS诊断测试,所以对这方面进行了研究学习,网上很少能查询到相关资料,故记录一下UDS自动化测试开发过程,由于保密原则,案例都是Demo,希望能帮到感兴趣的朋友 ...
- SQL:大表多表更新的两种方法
#标记不参与计算的明细(跨平台的或is_end=2)#跨平台订单:暂不处理 说明:大表即order_list_wx,几十万,需要根据小表(order_list_zfb ,几万)来做更新,查出两个平台都 ...
- vue+uniapp实现照录像,相册选择 | 图片裁剪压缩,视频压缩
一.插件简介 Zhimi-Camera(智密 - 智密 - 相机图册插件-视频/图片选择器)是一个支持拍照,录像,相册选择功能,自带图片裁剪,图片压缩,视频压缩,选择数量限制的uniapp原生插件.平 ...
- 基于 SoC 的卷积神经网络车牌识别系统设计(1)概述
NOTES: 这是第三届全国大学生集成电路创新创业大赛 - Arm 杯 - 片上系统设计挑战赛(本人指导的一个比赛).主要划分为以下的 Top5 重点.难点.亮点.热点以及创新点:1.通过 Arm C ...