题意:

给你一棵树,有两组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 * 网络流),这个复杂度是我猜的...

  1 #include <bits/stdc++.h>
  2
  3 const int N = 710, MO = 998244353, INF = 0x3f3f3f3f;
  4
  5 struct Edge {
  6     int nex, v;
  7     bool del;
  8 }edge[N << 1]; int tp = 1;
  9
 10 int f[N][N], n, e[N], siz[N], val[N], h[N], aim[N], p[1000010], top, small, root, root2, in_e[N], lm, in[N], d[N];
 11 bool vis[1000010];
 12 std::vector<int> v[N];
 13
 14 inline void getp(int n) {
 15     for(int i = 2; i <= n; i++) {
 16         if(!vis[i]) {
 17             p[++top] = i;
 18         }
 19         for(int j = 1; j <= top && i * p[j] <= n; j++) {
 20             vis[i * p[j]] = 1;
 21             if(i % p[j] == 0) {
 22                 break;
 23             }
 24         }
 25     }
 26     return;
 27 }
 28
 29 inline void add(int x, int y) {
 30     tp++;
 31     edge[tp].v = y;
 32     edge[tp].del = 0;
 33     edge[tp].nex = e[x];
 34     e[x] = tp;
 35     return;
 36 }
 37
 38 void getroot(int x, int f) {
 39     int large = 0;
 40     siz[x] = 1;
 41     for(int i = e[x]; i; i = edge[i].nex) {
 42         int y = edge[i].v;
 43         if(y == f) {
 44             continue;
 45         }
 46         in_e[y] = i;
 47         getroot(y, x);
 48         siz[x] += siz[y];
 49         large = std::max(large, siz[y]);
 50     }
 51     large = std::max(large, n - siz[x]);
 52     if(large < small) {
 53         root = x;
 54         root2 = 0;
 55         small = large;
 56     }
 57     else if(large == small) {
 58         root2 = x;
 59     }
 60     return;
 61 }
 62
 63 void DFS_1(int x, int f) {
 64     d[x] = d[f] + 1;
 65     v[d[x]].push_back(x);
 66     lm = std::max(lm, d[x]);
 67     h[x] = 1;
 68     for(int i = e[x]; i; i = edge[i].nex) {
 69         int y = edge[i].v;
 70         if(y == f || edge[i].del) {
 71             continue;
 72         }
 73         DFS_1(y, x);
 74         siz[x] += siz[y];
 75         h[x] = (h[x] + 1ll * h[y] * p[siz[y]] % MO) % MO;
 76     }
 77     return;
 78 }
 79
 80 namespace fl {
 81
 82     struct Edge {
 83         int nex, v, c, len;
 84         Edge(int N = 0, int V = 0, int C = 0, int L = 0) {
 85             nex = N;
 86             v = V;
 87             c = C;
 88             len = L;
 89         }
 90     }edge[2000010]; int tp = 1;
 91
 92     int e[N], d[N], vis[N], Time, pre[N], flow[N], n, tot;
 93     std::queue<int> Q;
 94
 95     inline void add(int x, int y, int z, int w) {
 96         //printf("addedge : x = %d y = %d z = %d w = %d \n", x, y, z, w);
 97         edge[++tp] = Edge(e[x], y, z, w);
 98         e[x] = tp;
 99         edge[++tp] = Edge(e[y], x, 0, -w);
100         e[y] = tp;
101         return;
102     }
103
104     inline bool SPFA(int s, int t) {
105         vis[s] = Time;
106         memset(d + 1, 0x3f, tot * sizeof(int));
107         flow[s] = INF;
108         d[s] = 0;
109         Q.push(s);
110         while(Q.size()) {
111             int x = Q.front();
112             Q.pop();
113             vis[x] = 0;
114             for(int i = e[x]; i; i = edge[i].nex) {
115                 int y = edge[i].v;
116                 if(d[y] > d[x] + edge[i].len && edge[i].c) {
117                     d[y] = d[x] + edge[i].len;
118                     flow[y] = std::min(edge[i].c, flow[x]);
119                     pre[y] = i;
120                     if(vis[y] != Time) {
121                         vis[y] = Time;
122                         Q.push(y);
123                     }
124                 }
125             }
126         }
127         return d[t] < INF;
128     }
129
130     inline void update(int s, int t) {
131
132         int f = flow[t];
133         while(s != t) {
134             int i = pre[t];
135             edge[i].c -= f;
136             edge[i ^ 1].c += f;
137             t = edge[i ^ 1].v;
138         }
139         return;
140     }
141
142     inline int solve(int x, int y) {
143
144         int ans = 0, cost = 0;
145         n = in[x] - (x != root);
146         int s = 2 * n + 1, t = tot = s + 1;
147         //printf("solve : x = %d  y = %d  n = %d \n", x, y, n);
148         memset(e + 1, 0, tot * sizeof(int));
149         tp = 1;
150
151         for(int i = ::e[x], cnt1 = 1; i; i = ::edge[i].nex, ++cnt1) {
152             int u = ::edge[i].v;
153             //printf("u = %d \n", u);
154             if(::d[u] < ::d[x] || ::edge[i].del) {
155                 --cnt1;
156                 continue;
157             }
158             add(s, cnt1, 1, 0);
159             add(cnt1 + n, t, 1, 0);
160             for(int j = ::e[y], cnt2 = 1; j; j = ::edge[j].nex, ++cnt2) {
161                 int v = ::edge[j].v;
162                 //printf("    v = %d \n", v);
163                 if(::d[v] < ::d[y] || ::edge[j].del) {
164                     --cnt2;
165                     continue;
166                 }
167                 /// u v
168                 if(f[u][v] > -INF) {
169                     add(cnt1, n + cnt2, 1, f[u][v]);
170                 }
171
172             }
173         }
174
175         ++Time;
176         while(SPFA(s, t)) {
177             //printf("inside --------------------- \n");
178             ans += flow[t];
179             cost += flow[t] * d[t];
180             update(s, t);
181             ++Time;
182         }
183
184         //printf("ans = %d cost = %d \n", ans, cost);
185         if(ans != n) {
186             return -INF;
187         }
188         else {
189             return cost + (val[x] != aim[y]);
190         }
191     }
192 }
193
194 int main() {
195
196     scanf("%d", &n);
197     for(int i = 1, x, y; i < n; i++) {
198         scanf("%d%d", &x, &y);
199         add(x, y);
200         add(y, x);
201         in[x]++;
202         in[y]++;
203     }
204     for(int i = 1; i <= n; i++) {
205         scanf("%d", &val[i]);
206     }
207     for(int i = 1; i <= n; i++) {
208         scanf("%d", &aim[i]);
209     }
210     root = root2 = 0;
211     small = N;
212     getroot(1, 0);
213     //printf("root 1 = %d  root 2 = %d \n", root, root2);
214
215     if(root2) {
216         ++n;
217         int i;
218         if(edge[in_e[root] ^ 1].v == root2) {
219             i = in_e[root];
220         }
221         else {
222             i = in_e[root2];
223         }
224         edge[i].del = edge[i ^ 1].del = 1;
225         add(n, root);
226         add(n, root2);
227         root = n;
228         in[n] = 2;
229     }
230     ///
231
232     //printf("root = %d \n", root);
233
234     DFS_1(root, 0);
235
236     for(int d = lm; d >= 1; d--) {
237         int len = v[d].size();
238         for(int i = 0; i < len; i++) {
239             int x = v[d][i];
240             for(int j = 0; j < len; j++) {
241                 int y = v[d][j];
242                 if(in[x] != in[y] || h[x] != h[y]) {
243                     f[x][y] = -INF;
244                     //printf("1 : ");
245                 }
246                 else {
247                     //f[x][y] = KM::solve(x, y);
248                     f[x][y] = fl::solve(x, y);
249                     //printf("2 : ");
250                 }
251                 //printf("f %d %d = %d \n", x, y, f[x][y]);
252             }
253         }
254     }
255
256     printf("%d\n", f[root][root]);
257     return 0;
258 }

AC代码

转载于:https://www.cnblogs.com/huyufeifei/p/10819455.html

洛谷P3296 刺客信条相关推荐

  1. 洛谷-题解 P2672 【推销员】

    独门思路!链表加优先队列! 这题一望,贪心是跑不掉了,但是我贪心并不好,所以想到了一个复杂一些但思路更保稳的做法 思路: 1 因为是离线操作,所以我们可以倒着求,先求x=n的情况,因为那样直接就知道了 ...

  2. 洛谷 P1142 轰炸

    洛谷 P1142 轰炸 题目描述 "我该怎么办?"飞行员klux向你求助. 事实上,klux面对的是一个很简单的问题,但是他实在太菜了. klux要想轰炸某个区域内的一些地方,它们 ...

  3. 洛谷 P1387 最大正方形

    P1387 最大正方形 题目描述 在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长. 输入输出格式 输入格式: 输入文件第一行为两个整数n,m(1<=n,m<=10 ...

  4. 洛谷P2763 试题库问题

    题目:https://www.luogu.org/problemnew/show/P2763 题目描述 «问题描述: 假设一个试题库中有n道试题.每道试题都标明了所属类别.同一道题可能有多个类别属性. ...

  5. 动态规划——洛谷_P1057传球游戏

    题目: 题目描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏.这次,老师带着同学们一起做传球游戏.游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球, ...

  6. 洛谷P1417 烹调方案

    洛谷P1417 烹调方案 如果是一般的01背包的话 选的先后是没关系的 但是这题选的先后是有关系的,因为他的价值是随着时间而变化的, 而你的01背包是做不到先选2再选1的 那么我们就跟国王游戏一样 用 ...

  7. 记忆优化搜索(简单题)(洛谷P3183 [HAOI2016]食物链 )( P5635 【CSGRound1】天下第一 )

    昨天做了蓝桥杯的时候,发现自己对于记忆优化搜索甚是不熟悉,所以今天随便找了几个基础题做做,顺便写下两片题解,顺便用了一下devc++敲的代码,发现没有代码补全真的可以说是灰常难受了... 洛谷P318 ...

  8. 洛谷 - 试炼场(全部题目备份)

    整理的算法模板合集: ACM模板 目录 1.新手村 1 - 1 洛谷的第一个任务 1 - 2 顺序与分支 1 - 3 循环!循环!循环! 1 - 4 数组 1 - 5 简单字符串 1 - 6 过程函数 ...

  9. 洛谷专题训练 ——【算法1-1】模拟与高精度

    洛谷题单[算法1-1]模拟与高精度 ACM-ICPC在线模板 题单链接: [算法1-1]模拟与高精度 下面的这一坨都是洛谷题单上的东东 题单简介 恭喜大家完成了第一部分语言入门,相信大家已经可以使用 ...

最新文章

  1. linux vi 清除所有,Vi命令:如何删除全部内容
  2. python3.x安装cv2失败
  3. Nacos客户端配置
  4. 漫步者lollipods如何调节音量_漫步者MF5扩音器体验:老师值得入手
  5. 个人作业(alpha)
  6. Atitit.swt 线程调用ui控件的方法
  7. 在字符串中查找id值MySQL
  8. Kubernetes Dashboard 终结者:KubeSphere
  9. windows_正确修改windows用户名(win10/win11)实践/副作用说明
  10. 【Unity3D Shader编程】之八 Unity5新版Shader模板源码解析径向模糊屏幕特效的实现
  11. 利用Jupyter Notebook进行科学计算和数据分析
  12. Windows7安装VM12时出现:安装程序无法继续 microsoft runtime dll
  13. MySQL(三) 完整性约束
  14. 霍兰德人格分析雷达图的两个坑
  15. Selective Search算法-候选框生成
  16. 用Python写一个天天酷跑
  17. 设置x轴文字换行展示
  18. Uniswap V2里的手续费换算
  19. 中科大日常交流英语期末考试话题汇总
  20. 什么是电弧光?电弧光保护的重要性

热门文章

  1. 实验详解——DNS反向解析、DNS主服务器和从服务器的配置
  2. Linux基础优化方法(四)———远程连接缓慢优化
  3. 网页游戏怎么修改数据_一周网页游戏开服数据总结(9.1-9.7)
  4. java7优化,七、索引优化分析
  5. python 完全面向对象_Python面向对象
  6. 查看mysql数据库版本_MySQL数据库之查看MySQL版本的四种方法
  7. 嵌入式android pdf,Embedded Android 英文原版PDF
  8. edittext无法获取焦点_小黑鞋强势回归,用帅气拉回时尚焦点
  9. linux开权限变绿了,解读Linux文件权限的设置方法
  10. mysql b 树 b树_MySQL B树和B+树的区别