您的位置:首页 > 产品设计 > UI/UE

HDU 4010 Query on The Trees

2014-10-17 17:51 323 查看
题意:

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

思路:

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
, tot;
int ch
[2], pre
, key
, add
, rev
, Max
;
bool rt
;

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;
else
ch[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);
else
Rotate(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路径+w
void 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数组 建立LCT
void 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: