题意:给出两棵同构的有根树,同构修改点的标号使得两棵树完全一样,至少需要修改多少次。

分析:肯定是将子树和另外一棵的某个子树对应,而两棵子树的问题是一个子问题,显然只有同构的子树才可以对应,这要用到 树hash 来判断同构。

树hash 形如:hash[u]=∑v∈son[u]hash[v]∗prime[son_size[v]]+1hash[u] =\displaystyle\sum_{v \in son[u]}hash[v]*prime[son\_size[v]] + 1hash[u]=v∈son[u]∑​hash[v]∗prime[son_size[v]]+1,通过 hash 值可以在不管标号的情况下唯一确定一棵树的形态。

转移显然是一个匹配问题,要使得匹配后代价和最小,可以用最小费用最大流。

注:如果求解的是两棵同构子树最少需要修改的次数使得他们相同,只能过94%,如果求解的是两棵同构子树最多相同的部分,才可以 AC


代码:

#include <bits/stdc++.h>
#include <stdio.h>
#include <string.h>
#define M 505
#define inf 0x3f3f3f3f
#define pii pair<int,int>
#define fir first
#define sec second
typedef long long ll;
const int maxn = 1010;
const int mod = 998244353;
using namespace std;
bool ispri[maxn * 10];
int pri[maxn * 10], n, dp[maxn][maxn], s, t;
struct MCMF {struct node{int v,c,w,rev;     //rev 是 反向弧(u,v),v 在 u 的位置。node(int vi = 0,int ci = 0,int wi = 0,int ri = 0) {v = vi;c = ci;w = wi;rev = ri;}};int dis[maxn],h[maxn],preV[maxn],preE[maxn];vector<node> g[maxn];vector<int> pot;         //点集 void init() {for(int i = 0; i < maxn; i++) g[i].clear();}void clear() {for (auto it : pot)g[it].clear(), h[it] = preV[it] = preE[it] = 0;pot.clear();}void add(int u,int v,int c,int w) {g[u].push_back(node(v,c,w,(int)g[v].size()));g[v].push_back(node(u,0,-w,(int)(g[u].size() - 1)));}int maxflow(int s,int t,int flow = inf) {int ans = 0,f = 0;/*fill(h,h + t + 1,0);fill(preV,preV + t + 1,0);fill(preE,preE + t + 1,0);*/while(flow) {priority_queue<pii,vector<pii>,greater<pii> > q;for (auto it : pot)dis[it] = inf;  //  fill(dis,dis + t + 1,inf);dis[s] = 0;q.push(pii(dis[s],s));while(!q.empty()) {pii now = q.top();q.pop();int u = now.sec;if(dis[u] < now.fir) continue;for(int i = 0; i < g[u].size(); i++) {int v = g[u][i].v,c = g[u][i].c,w = g[u][i].w;if(c && dis[v] > w + dis[u] + h[u] - h[v]) {preV[v] = u;preE[v] = i;dis[v] = w + dis[u] + h[u] - h[v];q.push(pii(dis[v],v));}}}        if(dis[t] == inf) break;  for (auto it : pot) h[it] += dis[it];//for(int i = 0; i <= t + 1; i++) h[i] += dis[i];int mx = inf;for(int i = t; i != s; i = preV[i])mx = min(mx,g[preV[i]][preE[i]].c);flow -= mx; f += mx; ans += h[t] * mx;for(int i = t; i != s; i = preV[i]) {g[preV[i]][preE[i]].c -= mx;g[i][g[preV[i]][preE[i]].rev].c += mx;}}return -ans;}
} p;
struct tree {vector<int>  g[maxn];int son[maxn], val[maxn], root;void add(int u,int v) {g[u].push_back(v);}void dfs(int u) {son[u] = 1; val[u] = 1;for (auto it : g[u]) {dfs(it);son[u] += son[it];val[u] = (val[u] + 1ll * val[it] * pri[son[it]] % mod) % mod;}}
} T[2];
void sieve(int n) {ispri[1] = ispri[0] = true;pri[0] = 0;for (int i = 2; i <= n; i++) {if (!ispri[i])pri[++pri[0]] = i;for (int j = 1; j <= pri[0] && i * pri[j] <= n; j++) {ispri[i * pri[j]] = true;if (i % pri[j] == 0) break;}}
}
int solve(int i,int j) {for (auto x : T[0].g[i]) {for (auto y : T[1].g[j]) {if (T[0].val[x] == T[1].val[y])dp[x][y] = solve(x,y);}}for (auto x : T[0].g[i])p.add(s,x,1,0), p.pot.push_back(x);for (auto y : T[1].g[j])p.add(n + y,t,1,0), p.pot.push_back(n + y);for (auto x : T[0].g[i]) {for (auto y : T[1].g[j]) {if (T[0].val[x] == T[1].val[y])p.add(x,n + y,1,-dp[x][y]);}}p.pot.push_back(s);p.pot.push_back(t);int ans = p.maxflow(s,t) + (i == j);p.clear();return ans;
}
int main () {sieve(5000);scanf("%d",&n);s = 2 * n + 2, t = 2 * n + 1;for (int i = 1, f; i <= n; i++) {scanf("%d",&f);if (f == 0) T[0].root = i;else T[0].add(f,i);}for (int i = 1, f; i <= n; i++) {scanf("%d",&f);if (f == 0) T[1].root = i;else T[1].add(f,i);}T[0].dfs(T[0].root);T[1].dfs(T[1].root);printf("%d\n",n - solve(T[0].root,T[1].root));return 0;
}

2020牛客多校10:Identical Trees(树hash + 树同构 + 费用流模板)相关推荐

  1. 牛客多校10 - Identical Trees(dp+二分图最小权匹配)

    题目链接:点击查看 题目大意:给出两个同构树 tree1 和 tree2 ,问最少需要改变多少个结点的标号,可以使得这两棵树相同 题目分析:直接 dfs 维护 dp 就好了,dp[ i ][ j ] ...

  2. 2020 牛客多校第一场

    2020 牛客多校第一场 A. B-Suffix Array 后缀数组的思想:倍增+桶排序的方式找出一串连续序列后缀的大小.虽说正常使用的时候都是字典序,但是只要修改排序方式,也能够达到一个类似的&q ...

  3. 2020牛客多校 J Easy Integration 计算过程详解【点火公式(Wallis积分)+组合数学】

    2020牛客暑期多校训练营(第一场) J Easy Integration 题 网上题解大多是 不断分部积分求解,其实有更为简洁的解法. 便是 Wallis积分(点火公式) + 组合数学 求解,本文给 ...

  4. [2020牛客多校第一场]Coda的题解集

    被暴打了. 会先写比赛中过题数100+的题目,其他的以后再补. 施工中,未完待续- F Infinite String Comparision 一开始想到的是对比到lcm(|a|,|b|),意料之中T ...

  5. 2020牛客多校第1场H-Minimum-cost Flow

    解题思路: 首先我们要从费用流mcmf的算法入手:因为它每次增广是再费用增广路上跑的,根据贪心的思想费用小的路基本上能运多少就尽量运多少,所以我们可以假设初始的边容量是1,只跑一遍mcmf.记录一下每 ...

  6. 2020牛客多校第1场H-Minimum-cost Flow-最小费用流

    https://ac.nowcoder.com/acm/contest/5666/H 题目大意:给出了每一条边的费用,有q个询问,问当每一条边的容量为u/v时,通过1流量的最小费用是多少. 思路:很明 ...

  7. 2020牛客多校联赛第四场 (BFH)

    文章目录 B:Basic Gcd Problem 题目 翻译 例子 大意 思路 代码 F:Finding the Order 题目 翻译 例子 大意 思路 代码 H:Harder Gcd Proble ...

  8. 数论分块 ---- 2020牛客多校第7场H-Dividing[思维+数论分块]

    题目大意: 解题思路:很明显满足条件的点是n%k==0∣∣n%k==1n\%k==0||n\%k==1n%k==0∣∣n%k==1 1.因为nnn是从111开始的如果一直乘以k[n=n∗k]k[n=n ...

  9. 2020牛客多校第7场C-A National Pandemic[树链剖分+思维]

    题目大意 1.首先我们看一下操作1:实际上可以说成在所有位置上加上w−dist(x,y)w-dist(x,y)w−dist(x,y),因为dist(x,x)=0dist(x,x)=0dist(x,x) ...

最新文章

  1. vim 如何将特定范围行注释掉,以及在末尾添加注释
  2. C#学习Error问题:“System.NullReferenceException:未将对象引用设置到对象的实例”
  3. cmd编译运行Java文件详解
  4. 单片机原理及其应用——单片机控制8只发光二极管交替闪烁
  5. BZOJ3998 TJOI2015弦论(后缀数组+二分答案)
  6. background-size
  7. HDU - 7054 Yiwen with Formula 分治拆位FFT + dp + 费马小定理降幂
  8. 牛客题霸 [二进制中1的个数] C++题解/答案
  9. 锁屏界面显示某些设置已隐藏_iOS 14 隐藏功能,只要轻点手机背面就能截屏
  10. Mybatis构建sql语法
  11. Comet杀人游戏开发日志-1(问题记录-于核心功能测试成功转向实际开发阶段)
  12. 蓝桥杯历年真题及答案汇总整理(Java、C语言、Python)
  13. js web端扫码枪对接
  14. 人工智能-马尔可夫模型
  15. Web排行榜相关排序算法总结
  16. Nvidia Jetson AGX Xavier 安装SATA接口固态硬盘
  17. 零代码爬虫神器 — Web Scraper 的使用
  18. Linux网络知识--DHCP服务(理论部分)
  19. c++/c语言 杨辉三角(详细讲解)。
  20. 5G网速真的有理论上那么高吗?

热门文章

  1. 电子商务网站的 10 个易用性规则
  2. 关于我们教育未来的一点感悟,一次实验课之后
  3. 硬件:主板知识详解:扩展插槽
  4. 中国主要咨询类型机构公司经营发展水平信息
  5. 苹果x微信语音十秒就断_通过微信汇报工作,你需要掌握这4点,让领导更加看重你...
  6. 删除苹果自带软件后果_苹果自带软件删了怎么恢复,快速恢复只需这三步
  7. rake- Ruby Make
  8. python画三维几何图形拼成的图案_Scratch3.0少儿编程案例:循环画窗花
  9. 年轻人疯狂追捧“临期食品”,餐饮人却担心“灰色地带”被暴露
  10. 匈奴未灭,何以家为?