[Noi2014]魔法森林 (Link Cut Tree)

DescriptionInput第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。Output输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。Sample Input

【输入样例1】 4 5 1 2 19 1 2 3 8 12 2 4 12 15 1 3 17 8 3 4 1 17

【输入样例2】 3 1 1 2 1 1

Sample Output

【输出样例1】 32 【样例说明1】 如果小E走路径1→2→4,,需要携带19+15=34个守护精灵; 如果小E走路径1→3→4,需要携带17+17=34个守护精灵; 如果小E走路径1→2→3→4,需要携带19+17=36个守护精灵; 如果小E走路径1→3→2→4,需要携带17+15=32个守护精灵。 综上所述,小E最少需要携带32个守护精灵。

【输出样例2】 -1 【样例说明2】 小E无法从1号节点到达3号节点,故输出-1。

HINT2<=n<=50,000 0<=m<=100,000 1<=ai ,bi<=50,000题解把边按a的值由小到大排序LCT维护边的b值和子树的max值,为什么要维护max下文会说,先说下LCT如何维护边的值: 设边。这样节点。设排序好的数组为: for (int i = 1; i <= m; i++) {int u = edges[i].u, v = edges[i].v;if (find(u) != find(v)) { // find和un都是并查集操作un(u, v);link(u, n + i);link(v, n + i);} else solve(i); // 找到环上最大的边将其删去if (find(1) == find(n)) ans = min(ans, val[query(1, n)] + edges[i].a);}

其实想通了还是蛮水的←_←

完整代码:using namespace std;const int MAXN = 50005;const int MAXM = 100005;int parent[MAXN];int find(int r) { return (r == parent[r]) ? r : parent[r] = find(parent[r]); }void un(int p, int q) { parent[find(p)] = find(q); }int n, m;struct Edge{int u, v, a, b;bool operator < (const Edge & e) const { return a < e.a; }} edges[MAXM];int val[MAXN + MAXM], mx[MAXN + MAXM];int tr[MAXN + MAXM][2];int fa[MAXN + MAXM];int q[MAXN + MAXM], top = 0;bool rev[MAXN + MAXM];bool isroot(int x) {return tr[fa[x]][0] != x && tr[fa[x]][1] != x;}void pushup(int x) {int l = tr[x][0], r = tr[x][1];mx[x] = x;if (val[mx[l]] > val[mx[x]]) mx[x] = mx[l];if (val[mx[r]] > val[mx[x]]) mx[x] = mx[r];}void pushdown(int x) {int l = tr[x][0], r = tr[x][1];if (rev[x]) {rev[x] ^= 1;rev[l] ^= 1;rev[r] ^= 1;swap(tr[x][0], tr[x][1]);}}void rotate(int x) {int y = fa[x], z = fa[y];int l, r;if (tr[y][0] == x) l = 0;else l = 1;r = l ^ 1;if (!isroot(y)) {if (tr[z][0] == y) tr[z][0] = x;else tr[z][1] = x;}fa[x] = z;fa[y] = x;fa[tr[x][r]] = y;tr[y][l] = tr[x][r];tr[x][r] = y;pushup(y);pushup(x);}void splay(int x) {top = 0;q[++top] = x;for (int i = x; !isroot(i); i = fa[i])q[++top] = fa[i];for (int i = top; i >= 1; i–)pushdown(q[i]);while (!isroot(x)) {int y = fa[x], z = fa[y];if (!isroot(y)) {if (tr[y][0] == x ^ tr[z][0] == y) rotate(x);else rotate(y);}rotate(x);}pushup(x);}void access(int x) {int t = 0;while (x) {splay(x);tr[x][1] = t;t = x;x = fa[x];}}void makeroot(int x) {access(x);splay(x);rev[x] ^= 1;}void ) {makeroot(x);fa[x] = y;}void cut() {makeroot(x);access(y);splay(y);tr[y][0] = fa[x] = 0;}) {makeroot(x);access(y);splay(y);return mx[y];}void solve(int k) {int u = edges[k].u, v = edges[k].v, w = edges[k].b;int t = query(u, v);if (w < val[t]) {cut(edges[t – n].u, t);cut(edges[t – n].v, t);link(u, k + n);link(v, k + n);}}int main() {scanf(“%d %d”, &n, &m);for (int i = 1; i <= n; i++) parent[i] = i;for (int i = 1; i <= m; i++)scanf(“, &edges[i].u, &edges[i].v, &edges[i].a, &edges[i].b);sort(edges + 1, edges + m + 1);for (int i = 1; i <= m; i++) {val[n + i] = edges[i].b;mx[n + i] = n + i;}int ans = INF;for (int i = 1; i <= m; i++) {int u = edges[i].u, v = edges[i].v;if (find(u) != find(v)) {un(u, v);link(u, n + i);link(v, n + i);} else solve(i);if (find(1) == find(n)) ans = min(ans, val[query(1, n)] + edges[i].a);}if (ans == INF) cout << -1 << endl;else cout << ans << endl;return 0;}

只要有信心,人永远不会挫败

[Noi2014]魔法森林 (Link Cut Tree)

相关文章:

你感兴趣的文章:

标签云: