题意

给你一棵树,有两组01权值a[]和b[]。n <= 700

你要构造一个自己到自己的映射,使得整棵树的形态不变,且映射后的a[]和映射之前的b[]中不同元素尽量少。

解:

发现这个整棵树形态不变......我们可能要用到树hash。

有个结论就是两棵树同构,当且仅当以它们重心为根的时候hash值相同。有两个重心就新建一个虚重心。

于是我们把重心搞出来当根,考虑映射之后的树,如果a映射到了b,那么a和b一定深度相同且hash值相同。

于是我们就按照深度分层,每层枚举点对,用f[a][b]来表示把点a映射到点b,子树内最少的不同元素。

于是如何求f[a][b]?发现我们要给a和b的若干个子节点进行匹配,要求权值最小。二分图匹配即可。我采用费用流实现。

复杂度:O(n³ + n * 网络流),这个复杂度是我猜的...

 #include <bits/stdc++.h>

 const int N = , MO = , INF = 0x3f3f3f3f;

 struct Edge {
int nex, v;
bool del;
}edge[N << ]; int tp = ; int f[N][N], n, e[N], siz[N], val[N], h[N], aim[N], p[], top, small, root, root2, in_e[N], lm, in[N], d[N];
bool vis[];
std::vector<int> v[N]; inline void getp(int n) {
for(int i = ; i <= n; i++) {
if(!vis[i]) {
p[++top] = i;
}
for(int j = ; j <= top && i * p[j] <= n; j++) {
vis[i * p[j]] = ;
if(i % p[j] == ) {
break;
}
}
}
return;
} inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].del = ;
edge[tp].nex = e[x];
e[x] = tp;
return;
} void getroot(int x, int f) {
int large = ;
siz[x] = ;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == f) {
continue;
}
in_e[y] = i;
getroot(y, x);
siz[x] += siz[y];
large = std::max(large, siz[y]);
}
large = std::max(large, n - siz[x]);
if(large < small) {
root = x;
root2 = ;
small = large;
}
else if(large == small) {
root2 = x;
}
return;
} void DFS_1(int x, int f) {
d[x] = d[f] + ;
v[d[x]].push_back(x);
lm = std::max(lm, d[x]);
h[x] = ;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == f || edge[i].del) {
continue;
}
DFS_1(y, x);
siz[x] += siz[y];
h[x] = (h[x] + 1ll * h[y] * p[siz[y]] % MO) % MO;
}
return;
} namespace fl { struct Edge {
int nex, v, c, len;
Edge(int N = , int V = , int C = , int L = ) {
nex = N;
v = V;
c = C;
len = L;
}
}edge[]; int tp = ; int e[N], d[N], vis[N], Time, pre[N], flow[N], n, tot;
std::queue<int> Q; inline void add(int x, int y, int z, int w) {
//printf("addedge : x = %d y = %d z = %d w = %d \n", x, y, z, w);
edge[++tp] = Edge(e[x], y, z, w);
e[x] = tp;
edge[++tp] = Edge(e[y], x, , -w);
e[y] = tp;
return;
} inline bool SPFA(int s, int t) {
vis[s] = Time;
memset(d + , 0x3f, tot * sizeof(int));
flow[s] = INF;
d[s] = ;
Q.push(s);
while(Q.size()) {
int x = Q.front();
Q.pop();
vis[x] = ;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(d[y] > d[x] + edge[i].len && edge[i].c) {
d[y] = d[x] + edge[i].len;
flow[y] = std::min(edge[i].c, flow[x]);
pre[y] = i;
if(vis[y] != Time) {
vis[y] = Time;
Q.push(y);
}
}
}
}
return d[t] < INF;
} inline void update(int s, int t) { int f = flow[t];
while(s != t) {
int i = pre[t];
edge[i].c -= f;
edge[i ^ ].c += f;
t = edge[i ^ ].v;
}
return;
} inline int solve(int x, int y) { int ans = , cost = ;
n = in[x] - (x != root);
int s = * n + , t = tot = s + ;
//printf("solve : x = %d y = %d n = %d \n", x, y, n);
memset(e + , , tot * sizeof(int));
tp = ; for(int i = ::e[x], cnt1 = ; i; i = ::edge[i].nex, ++cnt1) {
int u = ::edge[i].v;
//printf("u = %d \n", u);
if(::d[u] < ::d[x] || ::edge[i].del) {
--cnt1;
continue;
}
add(s, cnt1, , );
add(cnt1 + n, t, , );
for(int j = ::e[y], cnt2 = ; j; j = ::edge[j].nex, ++cnt2) {
int v = ::edge[j].v;
//printf(" v = %d \n", v);
if(::d[v] < ::d[y] || ::edge[j].del) {
--cnt2;
continue;
}
/// u v
if(f[u][v] > -INF) {
add(cnt1, n + cnt2, , f[u][v]);
} }
} ++Time;
while(SPFA(s, t)) {
//printf("inside --------------------- \n");
ans += flow[t];
cost += flow[t] * d[t];
update(s, t);
++Time;
} //printf("ans = %d cost = %d \n", ans, cost);
if(ans != n) {
return -INF;
}
else {
return cost + (val[x] != aim[y]);
}
}
} int main() { scanf("%d", &n);
for(int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
in[x]++;
in[y]++;
}
for(int i = ; i <= n; i++) {
scanf("%d", &val[i]);
}
for(int i = ; i <= n; i++) {
scanf("%d", &aim[i]);
}
root = root2 = ;
small = N;
getroot(, );
//printf("root 1 = %d root 2 = %d \n", root, root2); if(root2) {
++n;
int i;
if(edge[in_e[root] ^ ].v == root2) {
i = in_e[root];
}
else {
i = in_e[root2];
}
edge[i].del = edge[i ^ ].del = ;
add(n, root);
add(n, root2);
root = n;
in[n] = ;
}
/// //printf("root = %d \n", root); DFS_1(root, ); for(int d = lm; d >= ; d--) {
int len = v[d].size();
for(int i = ; i < len; i++) {
int x = v[d][i];
for(int j = ; j < len; j++) {
int y = v[d][j];
if(in[x] != in[y] || h[x] != h[y]) {
f[x][y] = -INF;
//printf("1 : ");
}
else {
//f[x][y] = KM::solve(x, y);
f[x][y] = fl::solve(x, y);
//printf("2 : ");
}
//printf("f %d %d = %d \n", x, y, f[x][y]);
}
}
} printf("%d\n", f[root][root]);
return ;
}

AC代码

洛谷P3296 刺客信条的更多相关文章

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

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

  2. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  3. 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...

  4. 洛谷P1108 低价购买[DP | LIS方案数]

    题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...

  5. 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP

    题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...

  6. 洛谷P1710 地铁涨价

    P1710 地铁涨价 51通过 339提交 题目提供者洛谷OnlineJudge 标签O2优化云端评测2 难度提高+/省选- 提交  讨论  题解 最新讨论 求教:为什么只有40分 数组大小一定要开够 ...

  7. 洛谷P1371 NOI元丹

    P1371 NOI元丹 71通过 394提交 题目提供者洛谷OnlineJudge 标签云端评测 难度普及/提高- 提交  讨论  题解 最新讨论 我觉得不需要讨论O long long 不够 没有取 ...

  8. 洛谷P1538迎春舞会之数字舞蹈

    题目背景 HNSDFZ的同学们为了庆祝春节,准备排练一场舞会. 题目描述 在越来越讲究合作的时代,人们注意的更多的不是个人物的舞姿,而是集体的排列. 为了配合每年的倒计时,同学们决定排出——“数字舞蹈 ...

  9. 洛谷八月月赛Round1凄惨记

    个人背景: 上午9:30放学,然后因为学校举办读书工程跟同学去书城选书,中午回来开始打比赛,下午又回老家,中间抽出一点时间调代码,回家已经8:50了 也许是7月月赛时“连蒙带骗”AK的太幸运然而因同学 ...

随机推荐

  1. 菲波那切数列(Fibonacci Number)

    什么是菲波那切数列?自己google一下,面试题里面经常遇到,考试递归算法用的. 在菲波那切数列中用递归不太好.第三种算法最好. 第一 递归算法最差了,不想说.测试一下,当N=6000时,半天出不来数 ...

  2. leetcode-92-反转链表②

    题目描述: 方法一: class Solution: def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode: ...

  3. springBoot_JPA和lombok

    一.JPA 特点:JPA可以根据方法名自动匹配sql语句. JPA是一个标准 Hibernate是JPA的一个实现,宗旨是尽量少写sql语句. 使用JPA 1.application.properti ...

  4. AtCoder F - Exhausted?

    传送门 sxy题解: //Achen #include<algorithm> #include<iostream> #include<cstring> #inclu ...

  5. 莫烦pytorch学习笔记(二)——variable

    .简介 torch.autograd.Variable是Autograd的核心类,它封装了Tensor,并整合了反向传播的相关实现 Variable和tensor的区别和联系 Variable是篮子, ...

  6. Maven的作用及简介

    Maven的作用及简介 一.maven作用 项目之间都是有依赖的,比如A项目依赖于B项目,B项目依赖与C.D项目,等等.这样的依赖链可能很长. 但是,没有一个项目的jar包我们都要导入进去,我们要做的 ...

  7. 过滤器filters

    <!DOCTYPE html> <html lang="zh"> <head> <title></title> < ...

  8. Gen8折腾日记

    (2019年2月19日注:这篇文章原先发在自己github那边的博客,时间是2016年7月7日,可惜在博客园这边不能修改发布时间.) 放假伊始,老大订购了两台服务器,一台是Dell的R630,用于其他 ...

  9. php 7.2下mcrypt扩展支持附带的问题

    按照网上提供的mcrypt扩展编译支持方法,完成了扩展编译,也确实可以正常加密/解密了 但是如果php.ini中配置为: error_reporting = E_ALL display_errors ...

  10. 小程序 设置tabBar选中颜色和图标

    "tabBar": { "selectedColor": "#4da9ff", //颜色 "list": [{ &quo ...