洛谷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 }