HDU 4010 Query on The Trees

来源:互联网 发布:淘宝刷粉丝怎么刷 编辑:程序博客网 时间:2024/05/17 15:59

题意:

一棵树  支持合并、分离、路径加权值、路径权值最大值

思路:

LCT入门题  也是我的第一道…  代码来源于kuangbin巨巨  我只是整理出自己的风格留作模版…

LCT比较好的入门资料是——《QTREE解法的一些研究》

LCT基本做法就是先dfs建树  然后根据输入做上述4个操作

对于合并  就是把u转到树根  然后接在v上

对于分离  就是把u转到splay的根  然后切断与左子树的连接

对于路径加值  就是求出lca  然后包含u和v的子树以及lca点进行加值

对于路径求最值  就是求出lca  然后和上面一样分三部分进行

代码:

#include<cstdio>#include<iostream>#include<cstring>#include<string>#include<algorithm>#include<map>#include<set>#include<vector>#include<queue>#include<cstdlib>#include<ctime>#include<cmath>using namespace std;#define N  300010#define L(x) (ch[x][0])#define R(x) (ch[x][1])struct Edge {int v, next;} ed[N * 2];int head[N], tot;int ch[N][2], pre[N], key[N], add[N], rev[N], Max[N];bool rt[N];void Update_Add(int u, int d) {if (!u)return;key[u] += d;add[u] += d;Max[u] += d;}void Update_Rev(int u) {if (!u)return;swap(L(u), R(u));rev[u] ^= 1;}void down(int u) {if (add[u]) {Update_Add(L(u), add[u]);Update_Add(R(u), add[u]);add[u] = 0;}if (rev[u]) {Update_Rev(L(u));Update_Rev(R(u));rev[u] = 0;}}void up(int u) {Max[u] = max(max(Max[L(u)], Max[R(u)]), key[u]);}//Rotate P Splay 一般不变void Rotate(int x) {int y = pre[x], kind = ch[y][1] == x;ch[y][kind] = ch[x][!kind];pre[ch[y][kind]] = y;pre[x] = pre[y];pre[y] = x;ch[x][!kind] = y;if (rt[y])rt[y] = false, rt[x] = true;elsech[pre[x]][ch[pre[x]][1] == y] = x;up(y);}//P函数先将splay根结点到u的路径上所有的结点的标记逐级下放void P(int u) {if (!rt[u])P(pre[u]);down(u);}void Splay(int u) {P(u);while (!rt[u]) {int fa = pre[u], ffa = pre[fa];if (rt[fa])Rotate(u);else if ((R(ffa) == fa) == (R(fa) == u))Rotate(fa), Rotate(u);elseRotate(u), Rotate(u);}up(u);}//将root到u的路径变成实边int Access(int u) {int v = 0;for (; u; u = pre[v = u]) {Splay(u);rt[R(u)] = true, rt[R(u) = v] = false;up(u);}return v;}//判断是否是同树(真实的树,非splay)bool judge(int u, int v) {while (pre[u])u = pre[u];while (pre[v])v = pre[v];return u == v;}//使u成为它所在的树的根void mroot(int u) {Access(u);Splay(u);Update_Rev(u);}//调用后u是原来u和v的lca,v和ch[u][1]分别存着lca的2个儿子(原来u和v所在的2颗子树)void lca(int &u, int &v) {Access(v), v = 0;while (u) {Splay(u);if (!pre[u])return;rt[R(u)] = true;rt[R(u) = v] = false;up(u);u = pre[v = u];}}//连接两棵树  u接在v上void link(int u, int v) {if (judge(u, v)) {puts("-1");return;}mroot(u);pre[u] = v;}//使u成为u所在树的根,并且v和它父亲的边断开void cut(int u, int v) {if (u == v || !judge(u, v)) {puts("-1");return;}mroot(u);Splay(v);pre[L(v)] = pre[v];pre[v] = 0;rt[L(v)] = true;L(v) = 0;up(v);}//u-v路径+wvoid ADD(int u, int v, int w) {if (!judge(u, v)) {puts("-1");return;}lca(u, v);Update_Add(R(u), w);Update_Add(v, w);key[u] += w;up(u);}//u-v路径最大值void query(int u, int v) {if (!judge(u, v)) {puts("-1");return;}lca(u, v);printf("%d\n", max(max(Max[v], Max[R(u)]), key[u]));}void addedge(int u, int v) {ed[tot].v = v;ed[tot].next = head[u];head[u] = tot++;}//利用dfs初始化pre数组  建立LCTvoid dfs(int u) {for (int i = head[u]; ~i; i = ed[i].next) {int v = ed[i].v;if (pre[v] != 0)continue;pre[v] = u;dfs(v);}}int main() {int n, q, u, v;while (scanf("%d", &n) == 1) {tot = 0;memset(head, -1, sizeof(head));memset(pre, 0, sizeof(pre));memset(ch, 0, sizeof(ch));memset(rev, 0, sizeof(rev));memset(add, 0, sizeof(add));for (int i = 0; i <= n; i++)rt[i] = true;Max[0] = -2000000000;for (int i = 1; i < n; i++) {scanf("%d%d", &u, &v);addedge(u, v);addedge(v, u);}for (int i = 1; i <= n; i++) {scanf("%d", &key[i]);Max[i] = key[i];}scanf("%d", &q);pre[1] = -1;dfs(1);pre[1] = 0;int op;while (q--) {scanf("%d", &op);if (op == 1) {int x, y;scanf("%d%d", &x, &y);link(x, y);} else if (op == 2) {int x, y;scanf("%d%d", &x, &y);cut(x, y);} else if (op == 3) {int w, x, y;scanf("%d%d%d", &w, &x, &y);ADD(x, y, w);} else {int x, y;scanf("%d%d", &x, &y);query(x, y);}}printf("\n");}return 0;}


0 0
原创粉丝点击