题意

给你一棵树,有两组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. 3.在vm上安装centos 7

    在vm上安装centos 7 1.文件 → 新建虚拟机 3.选择安装Linux系统 4. 虚拟机命名,并选择安装的文件夹 5.选择分配的处理器 6.使用网络地址转换 7.默写选项 9.新建虚拟机 10 ...

  2. LeetCode 28.实现strStr()(Python3)

    题目: 实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存 ...

  3. Crontab- Linux必学的60个命令

    1.作用 使用crontab命令可以修改crontab配置文件,然后该配置由cron公用程序在适当的时间执行,该命令使用权限是所有用户. 2.格式 crontab [ -u user ] 文件 cro ...

  4. vue初学之node.js安装、cnpm安装、vue初体验

    1. 如果本机没有安装node运行环境,请下载node 安装包进行安装.地址:https://nodejs.org/en/ 2.装完,使用cmd命令行输入:node -v回车 如果输出版本号则成功. ...

  5. antidependence and data hazard

    See below example. ADDD  F6, F0, F8 SUBD   F8, F10, F14 Some article would say that “ There’s an ant ...

  6. scoreboarding

    Reference docs: https://en.wikipedia.org/wiki/Scoreboarding SSC_course_5_Scoreboard_ex.pdf 1, what i ...

  7. 数组(Array)与 字符串(String)公用的属性与方法

    数组与字符串都有很多方法,有一些方法是公用的,在这里就将数组与字符串公用的方法提取出来,方便大家的记忆 1. length 可通过str.length与arr.length分别取到字符串与数组的长度: ...

  8. CF1097E Egor and an RPG game

    最少反链划分数 = 最长链.实现:每次找出所有极大元作为一个反链. 任意长度小于k * (k + 1) / 2的排列都能被划分为不多于k个单调序列.且这是一个紧的上界. 然后这题就可以切了. 题意:给 ...

  9. 正则获取html标签字符串中图片地址

    html标签字符串: var htmlStr = "<div class='testClass'><img=http://www.chinanews.com/part/ho ...

  10. sql自定义日期函数,返回范围内日期和星期数表。

    Create function [dbo].[FUN_GenerateTime] ( @begin_date datetime, -- 起始时间 @end_date datetime -- 结束时间 ...