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救星了 至于一定在直径中的边数,可以发现这些边一定是连续的(不然你两条直径中间能有空挡?),然后,如果某个点往下有多条直径,那么这条点以下都不算入答案.所以以直径分别两端点为根,找出 ...
随机推荐
- oracle(创建数据库对象)
1 --创建数据库 2 --1.SYSDBA系统权限 3 startup:--启动数据库. 4 shutdown:--关闭数据库. 5 alter database[mount]|[open]|[ba ...
- mysql 将A表的a字段赋值给B表的b字段
A.B为2个不同的表,将A表的a字段赋值给B表的b字段:update B set b = (select a from A where A.id = B.id) where B.b is null 注 ...
- 【Linux】【Problems】在fedora 9上解决依赖问题
summary: 在32bit的fedora9上安装EMC客户端遇到无法解决的依赖问题 detail: rpm 安装: [root@hcszmons02 tmp]# rpm -ivh lgtoclnt ...
- Linux系统的文件复制移动删除与VIM编辑
目录 今日内容概要 内容详细 复制文件 移动文件 删除文件 系统别名(针对 rm 改别名) vim编辑器 今日内容概要 复制文件 移动文件 删除文件 vim编辑器 内容详细 复制文件 # 命令: cp ...
- Linux core 文件浅析
浅析Linux下core文件 当我们的程序崩溃时,内核有可能把该程序当前内存映射到core文件里,方便程序员找到程序出现问题的地方.最常出 现的,几乎所有C程序员都出现过的错误就是"段错误& ...
- 开会不用把人都轰进一个小黑屋子——《Office妖精是怎样炼成的》续2
<Office妖精是怎样炼成的>http://blog.sina.com.cn/s/articlelist_1446470001_6_1.html 一本不是技术图书却含有技术内容的图书,一 ...
- 日历共享(Project)
<Project2016 企业项目管理实践>张会斌 董方好 编著 为了某一个项目,我们建好了一整套的日历,除了标准日历里加上了所有的假期以外,其他来自火星的水星的金星的土星的木星的BT的不 ...
- .NET Core工程应用系列(1) 定制化Audit.NET实现自定义AuditTarget
需求背景 最近在项目上需要增加对用户操作进行审计日志记录的功能,调研了一圈,在.net core生态里,用的最多的是Audit.NET.浏览完这个库的文档后,觉得大致能满足我们的诉求,于是建立一个控制 ...
- java多线程9:线程池
线程池 线程池的优点 我们知道线程的创建和上下文的切换也是需要消耗CPU资源的,所以在多线程任务下,使用线程池的优点就有: 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. ...
- CF1433B Yet Another Bookshelf 题解
Content 在一个仅有 \(0,1\) 这两个数的数列上,每次可以选择一段全为1的连续区间将其左移 \(1\) 或者右移 \(1\).现给出 \(t\) 次询问,每次询问给出一个长度为 \(n\) ...