[SDOI 2019]世界地图[虚树][MST]
菜啊,被迫文化课还天天颓,不知道过了多久才补上这篇题解,但是 zhq 讲的真好
简述题意:给定一张 $n\times m$ 的网格图,其中最左端的一排点和最右端的一排点再相连,形成一个圆柱形网格图,边有边权。可以将横纵线理解为经纬线
然后有 q 次询问,每次删除经度在 $[l, r]$ 这个区间的图,求剩下部分的 MST(最小生成树)
数据范围:$n\leq 100, m\leq 10000, q\leq 10000$
可以想到,对于一次询问的图,其实就是 以第一排点开始的某个前缀图 + 以第 m 排点开始的某个后缀图 + 第 1 排点与第 m 排点相连的边 形成的一张图的 MST
求出每一个前缀图的 MST,再求出每一个后缀图的 MST,加上 $1\rightarrow m$ 的那一排边,这样会形成很多环,来考虑怎样将这些环搞掉
因为这些图是网格图,所以对于一个环,只需要删除掉这个环上边权最大的边即可
但是这样显然复杂度爆炸,实际上可能被删除的边有 $O(N^2)$ 条?其实是 $O(N)$ 条
来考虑虚树
对于前缀图,我们将第 1 排点全部设为 key 点;对于后缀图,我们将第 m 排点全部设为 key 点,在 MST 上形成一棵虚树
这样的一棵虚树的一条边表示原图 MST 上的一条链
可以从构造上发现,对于这样的一棵虚树,它只有 $O(N)$ 条边和 $O(N)$ 个点
(原因是因为每个非叶子节点都至少有两个儿子,如同 SAM 只有 O(N) 个点的原因 —— zhq)
这样我们虚树上的每一条边表示它所代表的那一条链上边权最大的边的边权
然后对于整张图(前缀虚树+后缀虚树+一排边)求一遍 MST 即可
需要注意的是,我们是在虚树上求 MST,所以这个 MST 并不是答案,原树上还有不少不可能被删除的边没有被统计
那我们可以额外记录一下,不可能被删除的边的边权和
可能被删的边只有虚树上的边以及那一排边
那么最后答案就是 = 前缀图中一定不可能被删的边权和 + 后缀图中一定不可能被删的边权和 + (前缀虚树+后缀虚树+一排边 的 MST)
好像做完了
但是还有个问题没有解决:怎样求出前缀图虚树和后缀图虚树
以前缀为例子:假设我们已经求出了 i-1 的前缀图的虚树,考虑如何求出 i 的前缀图的虚树
其实和上面计算答案的方法一样,把 i-1 的那个前缀图当作计算答案时的后缀图,第 i 排点是计算答案时的前缀图,再加上 $(i-1)\rightarrow i$ 那一排边
按照上面计算答案的算法做,即可完成。后缀同理
另外需要注意的是,这道题我(zhq)所用的建造虚树的方法并不适用于所有虚树构造
复杂度:大概是 $O(nm+qn)$?
代码(适当的加了几个注释):
1 // #3112. 「SDOI2019」世界地图 2 // MST + 虚树 3 4 #include <ctime> 5 #include <cmath> 6 #include <cstdio> 7 #include <cstring> 8 #include <cstdlib> 9 #include <exception> 10 #include <iostream> 11 #include <algorithm> 12 #include <vector> 13 #include <queue> 14 #define inf 10010 15 #define N 110 16 #define INF 0x7fffffff 17 #define ll long long 18 19 namespace chiaro{ 20 21 template <class I> 22 inline void read(I &num){ 23 num = 0; char c = getchar(), up = c; 24 while(c < '0' || c > '9') up = c, c = getchar(); 25 while(c >= '0' && c <= '9') num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar(); 26 up == '-' ? num = -num : 0; return; 27 } 28 template <class I> 29 inline void read(I &a, I &b) {read(a); read(b);} 30 template <class I> 31 inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);} 32 33 struct MSTedge { 34 int from; 35 int to; 36 int val; 37 MSTedge() {} 38 MSTedge(int from, int to, int val) { 39 this->from = from; 40 this->to = to; 41 this->val = val; 42 } 43 }; 44 45 struct edge { 46 int to; 47 int val; 48 edge* next; 49 }; 50 51 int n, m; 52 ll ansdis; 53 int id[N][inf]; // id 54 int right[N][inf], down[N][inf]; // 向右边权 & 向下边权 55 bool isKey[inf * N]; // 是否是 key 点 56 edge* g[inf * N]; 57 58 int fa[inf * N]; 59 60 struct MST { 61 ll sum; // MST 上不可能被删的边的边权之和 62 std::vector <MSTedge> e; 63 64 MST() {} 65 MST(int now) { // 搞出 now 那一列点的 MST 66 sum = 0; 67 for(int i = 1; i < n; i++) 68 e.push_back(MSTedge(id[i][now], id[i + 1][now], down[i][now])); 69 return; 70 } 71 72 inline int size() {return e.size();} 73 }; 74 75 MST pre[inf], suf[inf]; // 前缀 & 后缀 76 77 bool edgeflag; 78 inline void connect(int from, int to, int val){ 79 static edge pool[inf * N]; 80 static edge* p = pool; 81 if(edgeflag) p = pool, edgeflag = 0; // 清空 82 p->to = to; 83 p->val = val; 84 p->next = g[from]; 85 g[from] = p; p++; 86 return; 87 } 88 89 namespace gen { 90 unsigned int SA, SB, SC;int lim; 91 int getweight() { 92 SA ^= SA << 16; 93 SA ^= SA >> 5; 94 SA ^= SA << 1; 95 unsigned int t = SA; 96 SA = SB; 97 SB = SC; 98 SC ^= t ^ SA; 99 return SC % lim + 1; 100 } 101 102 void gen() { 103 read(n, m); 104 read(SA, SB, SC); 105 read(lim); 106 int i, j, w; 107 for(i = 1; i <= n; i++) 108 for(j = 1; j <= m; j++) { 109 w = getweight(); 110 right[i][j] = w; 111 } 112 for(i = 1; i < n; i++) 113 for(j = 1; j <= m; j++) { 114 w = getweight(); 115 down[i][j] = w; 116 } 117 int index = 0; 118 for(int i = 1; i <= n; i++) { 119 for(int j = 1; j <= m; j++) id[i][j] = ++index; 120 } 121 return; 122 } 123 } 124 125 int findf(int f) {return ((f == fa[f]) ? (f) : (fa[f] = findf(fa[f])));} 126 127 inline void clear(const std::vector <MSTedge>& V) { 128 for(unsigned int i = 0; i < V.size(); i++) { 129 fa[V[i].from] = V[i].from; 130 fa[V[i].to] = V[i].to; 131 g[V[i].from] = NULL; 132 g[V[i].to] = NULL; 133 } 134 edgeflag = 1; 135 return; 136 } 137 138 inline bool cmp(MSTedge& a, MSTedge& b) {return a.val < b.val;} 139 140 inline ll kruskal(std::vector <MSTedge>& V) { 141 srand(time(0)); 142 ll ans = 0; 143 std::sort(V.begin(), V.end(), cmp); 144 for(unsigned int i = 0; i < V.size(); i++) { 145 int u = V[i].from, v = V[i].to, w = V[i].val; 146 int fau = findf(u); 147 int fav = findf(v); 148 if(fav == fau) continue; 149 ans += w, ansdis += w; // ansdis 存 MST 中不会被删的边的边权 150 // 我们先将他定为 MST 边权和,再在后面减去新虚树中的边的边权 151 (rand() & 1) ? (fa[fav] = fau) : (fa[fau] = fav); 152 connect(u, v, w); 153 connect(v, u, w); 154 } 155 return ans; 156 } 157 158 bool buildVT(int now, int f) { // 处理出所有 key 点 159 int tot = isKey[now]; 160 // tot 是 自己是否是key点 与 有key点的子树 的数量和 161 for(edge* e = g[now]; e; e = e->next) { 162 int to = e->to; 163 if(to == f) continue; 164 tot += buildVT(to, now); 165 } 166 if(tot >= 2) isKey[now] = 1; 167 return !!tot; 168 } 169 170 // 将上面处理出来的 key 连边形成虚树 171 // last 是上面的那个 key 点, max 是路径上的权值最大值 172 void processVal(int now, int f, int last, int max, std::vector <MSTedge>& V) { 173 if(isKey[now]) { 174 if(last) { 175 V.push_back(MSTedge(now, last, max)); 176 ansdis -= max; 177 } 178 last = now, max = 0; 179 } 180 for(edge* e = g[now]; e; e = e->next) { 181 int to = e->to; 182 if(to == f) continue; 183 processVal(to, now, last, std::max(max, e->val), V); 184 } 185 return; 186 } 187 188 #define nextpos(x) ((x == m) ? (1) : (x + 1)) 189 190 // type: 1(pre), 2(suf), 3(process answer) 191 // 将 a 与 b 与 x 那一列向右的一排边合并 192 inline MST merge(MST& a, MST& b, int x, int type) { 193 static std::vector <MSTedge> V; 194 // 存储 a虚树 与 b虚树 与 x 向右的那一排边 195 MST ans; ansdis = 0; V.clear(); 196 for(int i = 0; i < a.size(); i++) V.push_back(a.e[i]); 197 for(int i = 0; i < b.size(); i++) V.push_back(b.e[i]); 198 for(int i = 1; i <= n; i++) 199 V.push_back(MSTedge(id[i][x], id[i][nextpos(x)], right[i][x])); 200 201 clear(V); 202 ll mstval = kruskal(V); 203 204 if(type == 3) { // 前后缀合并那就直接做完了 205 ans.sum = a.sum + b.sum + mstval; 206 return ans; 207 } else if(type == 1) { // 后面两种情况就开始求 虚树 208 for(int i = 1; i <= n; i++) { 209 isKey[id[i][1]] = 1; 210 isKey[id[i][x + 1]] = 1; 211 } 212 } else { 213 for(int i = 1; i <= n; i++) { 214 isKey[id[i][m]] = 1; 215 isKey[id[i][x]] = 1; 216 } 217 } 218 219 buildVT(V[0].from, 0); // 处理虚树 220 V.clear(); 221 processVal(V[0].from, 0, 0, 0, V); // 处理出虚树 222 for(unsigned int i = 0; i < V.size(); i++) 223 isKey[V[i].from] = isKey[V[i].to] = 0; 224 ans.e = V; 225 ans.sum = a.sum + b.sum + ansdis; 226 return ans; 227 } 228 229 inline void getPre() { 230 pre[1] = MST(1); 231 for(int i = 2; i <= m; i++) { 232 pre[i] = MST(i); 233 pre[i] = merge(pre[i - 1], pre[i], i - 1, 1); 234 } 235 return; 236 } 237 238 inline void getSuf() { 239 suf[m] = MST(m); 240 for(int i = m - 1; i; i--) { 241 suf[i] = MST(i); 242 suf[i] = merge(suf[i + 1], suf[i], i, 2); 243 } 244 return; 245 } 246 247 inline void setting(){ 248 #ifndef ONLINE_JUDGE 249 freopen("_test.in", "r", stdin); 250 freopen("_test.out", "w", stdout); 251 #endif 252 return; 253 } 254 255 inline int main(){ 256 setting(); 257 gen::gen(); 258 getPre(); getSuf(); 259 int T; read(T); 260 while(T--) { 261 int l, r; read(l, r); 262 printf("%lld\n", merge(suf[r + 1], pre[l - 1], m, 3).sum); 263 } 264 return 0; 265 } 266 267 } 268 269 signed main(){ return chiaro::main();}

浙公网安备 33010602011771号