洛谷P3296 刺客信条

题意

给你一棵树,有两组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代码

 

posted @ 2019-05-06 14:33  huyufeifei  阅读(242)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜