题目链接:BZOJ - 1036

题目分析

这道题可以用树链剖分,块状树等多种方法解决,也可以使用 LCT。

修改某个点的值时,先将它 Splay 到它所在的 Splay 的根,然后修改它的值,再将它 Update 一下。

(1)

询问 x, y 两点之间的路径时,假设 x 是深度小的那一个,先 Access(x) ,然后再 Access(y) 的返回值就是 x, y 的 LCA 。

这时从 x 到 LCA 的路径已经在 LCA 处断开了。我们将 x Splay 一下,然后就是 x 所在的 Splay, LCA, Son[LCA][1] 这 3 部分组成了 x, y 的路径。

要特判 x == LCA 的情况。

(2)

在询问 x, y 两点之间的路径时,也可以用下面的方法:

将 x 变为这棵树的根,然后 Access(y) ,就是求 y 到根的路径了。

为了实现把一个点变为根,需要将这个点到当前树根的路径翻转,就是说,Access(x) , Splay(x), Reverse(x) 。

然后需要注意的就是翻转标记的下传,当要改变 Splay 的形态之前,就需要将涉及到的点 PushDown 一下,使他们没有标记。

比如 Rotate 的时候,或者 Access 中断掉 x 的右子树之前。一定要注意!

Rotate(x) 时, y = Father[x] ,要先 PushDown(y); PushDown(x); 然后再做下面的操作。

代码

不换根的代码:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm> using namespace std; inline void Read(int &Num)
{
char c = getchar();
bool Neg = false;
while (c < '0' || c > '9')
{
if (c == '-') Neg = true;
c = getchar();
}
Num = c - '0'; c = getchar();
while (c >= '0' && c <= '9')
{
Num = Num * 10 + c - '0';
c = getchar();
}
if (Neg) Num = -Num;
} const int MaxN = 30000 + 5, INF = 999999999; int n, q, Ans;
int A[MaxN], Father[MaxN], Sum[MaxN], Max[MaxN], Son[MaxN][2], Depth[MaxN]; bool isRoot[MaxN]; struct Edge
{
int v;
Edge *Next;
} E[MaxN * 2], *P = E, *Point[MaxN]; inline void AddEdge(int x, int y)
{
++P; P -> v = y;
P -> Next = Point[x]; Point[x] = P;
} void DFS(int x, int Fa, int Dep)
{
Father[x] = Fa; Depth[x] = Dep;
for (Edge *j = Point[x]; j; j = j -> Next)
{
if (j -> v == Fa) continue;
DFS(j -> v, x, Dep + 1);
}
} inline int gmin(int a, int b) {return a < b ? a : b;}
inline int gmax(int a, int b) {return a > b ? a : b;} inline void Update(int x)
{
Max[x] = gmax(A[x], gmax(Max[Son[x][0]], Max[Son[x][1]]));
Sum[x] = A[x] + Sum[Son[x][0]] + Sum[Son[x][1]];
} void Rotate(int x, int f)
{
int y = Father[x];
if (isRoot[y])
{
isRoot[y] = false;
isRoot[x] = true;
}
else
{
if (y == Son[Father[y]][0]) Son[Father[y]][0] = x;
else Son[Father[y]][1] = x;
}
Father[x] = Father[y];
Son[y][f ^ 1] = Son[x][f];
if (Son[x][f]) Father[Son[x][f]] = y;
Son[x][f] = y;
Father[y] = x;
Update(y);
Update(x);
} void Splay(int x)
{
int y;
while (!isRoot[x])
{
y = Father[x];
if (isRoot[y])
{
if (x == Son[y][0]) Rotate(x, 1);
else Rotate(x, 0);
break;
}
if (y == Son[Father[y]][0])
{
if (x == Son[y][0])
{
Rotate(y, 1);
Rotate(x, 1);
}
else
{
Rotate(x, 0);
Rotate(x, 1);
}
}
else
{
if (x == Son[y][1])
{
Rotate(y, 0);
Rotate(x, 0);
}
else
{
Rotate(x, 1);
Rotate(x, 0);
}
}
}
} int Access(int x)
{
int y = 0;
while (x != 0)
{
Splay(x);
isRoot[Son[x][1]] = true;
Son[x][1] = y;
if (y) isRoot[y] = false;
Update(x);
y = x;
x = Father[x];
}
return y;
} char Str[10]; int main()
{
scanf("%d", &n);
int a, b, LCA;
for (int i = 1; i <= n - 1; ++i)
{
Read(a); Read(b);
AddEdge(a, b); AddEdge(b, a);
}
DFS(1, 0, 0);
for (int i = 1; i <= n; ++i)
{
Read(A[i]);
isRoot[i] = true;
Sum[i] = Max[i]= A[i];
}
Sum[0] = 0; Max[0] = -INF;
scanf("%d", &q);
for (int i = 1; i <= q; ++i)
{
scanf("%s", Str);
Read(a); Read(b);
if (strcmp(Str, "CHANGE") == 0)
{
Splay(a);
A[a] = b;
Update(a);
}
else if (strcmp(Str, "QMAX") == 0)
{
if (Depth[a] > Depth[b]) swap(a, b);
Access(a);
LCA = Access(b);
Splay(a);
if (a == LCA) Ans = gmax(A[LCA], Max[Son[LCA][1]]);
else Ans = gmax(A[LCA], gmax(Max[a], Max[Son[LCA][1]]));
printf("%d\n", Ans);
}
else if (strcmp(Str, "QSUM") == 0)
{
if (Depth[a] > Depth[b]) swap(a, b);
Access(a);
LCA = Access(b);
Splay(a);
if (a == LCA) Ans = A[LCA] + Sum[Son[LCA][1]];
else Ans = A[LCA] + Sum[a] + Sum[Son[LCA][1]];
printf("%d\n", Ans);
}
}
return 0;
}

换根的代码:

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm> using namespace std; inline void Read(int &Num)
{
char c = getchar();
bool Neg = false;
while (c < '0' || c > '9')
{
if (c == '-') Neg = true;
c = getchar();
}
Num = c - '0'; c = getchar();
while (c >= '0' && c <= '9')
{
Num = Num * 10 + c - '0';
c = getchar();
}
if (Neg) Num = -Num;
} inline int gmin(int a, int b) {return a < b ? a : b;}
inline int gmax(int a, int b) {return a > b ? a : b;} const int MaxN = 30000 + 5, INF = 999999999; int n, q;
int A[MaxN], Father[MaxN], Son[MaxN][2], Max[MaxN], Sum[MaxN]; bool isRoot[MaxN], Rev[MaxN]; struct Edge
{
int v;
Edge *Next;
} E[MaxN * 2], *P = E, *Point[MaxN]; inline void AddEdge(int x, int y)
{
++P; P -> v = y;
P -> Next = Point[x]; Point[x] = P;
} void DFS(int x, int Fa)
{
Father[x] = Fa;
for (Edge *j = Point[x]; j; j = j -> Next)
{
if (j -> v == Fa) continue;
DFS(j -> v, x);
}
} inline void Reverse(int x)
{
Rev[x] = !Rev[x];
swap(Son[x][0], Son[x][1]);
} inline void PushDown(int x)
{
if (!Rev[x]) return;
Rev[x] = false;
if (Son[x][0]) Reverse(Son[x][0]);
if (Son[x][1]) Reverse(Son[x][1]);
} inline void Update(int x)
{
Sum[x] = Sum[Son[x][0]] + Sum[Son[x][1]] + A[x];
Max[x] = gmax(A[x], gmax(Max[Son[x][0]], Max[Son[x][1]]));
} void Rotate(int x)
{
int y = Father[x], f;
PushDown(y);
PushDown(x);
if (x == Son[y][0]) f = 1;
else f = 0;
if (isRoot[y])
{
isRoot[y] = false;
isRoot[x] = true;
}
else
{
if (y == Son[Father[y]][0]) Son[Father[y]][0] = x;
else Son[Father[y]][1] = x;
}
Father[x] = Father[y];
Son[y][1 ^ f] = Son[x][f];
if (Son[x][f]) Father[Son[x][f]] = y;
Son[x][f] = y;
Father[y] = x;
Update(y);
Update(x);
} void Splay(int x)
{
int y;
while (!isRoot[x])
{
y = Father[x];
if (isRoot[y])
{
Rotate(x);
break;
}
if (y == Son[Father[y]][0])
{
if (x == Son[y][0])
{
Rotate(y);
Rotate(x);
}
else
{
Rotate(x);
Rotate(x);
}
}
else
{
if (x == Son[y][1])
{
Rotate(y);
Rotate(x);
}
else
{
Rotate(x);
Rotate(x);
}
}
}
} int Access(int x)
{
int y = 0;
while (x != 0)
{
Splay(x);
PushDown(x);
isRoot[Son[x][1]] = true;
Son[x][1] = y;
if (y) isRoot[y] = false;
Update(x);
y = x;
x = Father[x];
}
return y;
} void Make_Root(int x)
{
int t = Access(x);
Reverse(t);
} int main()
{
scanf("%d", &n);
int a, b, c;
for (int i = 1; i <= n - 1; ++i)
{
Read(a); Read(b);
AddEdge(a, b); AddEdge(b, a);
}
for (int i = 1; i <= n; ++i) Read(A[i]);
DFS(1, 0);
Max[0] = -INF; Sum[0] = 0;
for (int i = 1; i <= n; ++i)
{
isRoot[i] = true;
Sum[i] = Max[i] = A[i];
}
scanf("%d", &q);
char Str[10];
int Ans, LCA;
for (int i = 1; i <= q; ++i)
{
scanf("%s", Str);
Read(a); Read(b);
if (strcmp(Str, "CHANGE") == 0)
{
Splay(a);
A[a] = b;
Update(a);
}
else
{
Make_Root(a);
c = Access(b);
if (strcmp(Str, "QMAX") == 0) printf("%d\n", Max[c]);
else printf("%d\n", Sum[c]);
}
}
return 0;
}

  

[BZOJ 1036] [ZJOI2008] 树的统计Count 【Link Cut Tree】的更多相关文章

  1. BZOJ.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)

    BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值) 题意分析 (题目图片来自于 这里) 第一道树链剖分的题目,谈一下自己的理解. 树链剖分能解决的问题是,题目 ...

  2. BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit ...

  3. BZOJ 1036: [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 14354  Solved: 5802 [Subm ...

  4. bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10677  Solved: 4313[Submit ...

  5. Bzoj 1036: [ZJOI2008]树的统计Count 树链剖分,LCT

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 11102  Solved: 4490[Submit ...

  6. 数据结构(LCT动态树):BZOJ 1036: [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 12266  Solved: 4945[Submit ...

  7. BZOJ 1036: [ZJOI2008]树的统计Count( 树链剖分 )

    树链剖分... 不知道为什么跑这么慢 = = 调了一节课啊跪.. ------------------------------------------------------------------- ...

  8. bzoj 1036: [ZJOI2008]树的统计Count 树链剖分+线段树

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 16294  Solved: 6645[Submit ...

  9. bzoj 1036: [ZJOI2008]树的统计Count (树链剖分+线段树 点权)

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 21194  Solved: 8589[Submit ...

  10. BZOJ 1036: [ZJOI2008]树的统计Count (树链剖分模板题)

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14982  Solved: 6081[Submit ...

随机推荐

  1. myeclipse开发代码颜色搭配保护视力

    废话不多说,这个东西主要是为了保护视力的,另外我也挺喜欢上边的颜色搭配的,今天特拿出来分享.直接上图

  2. iOS判断iPhone型号

    链接: http://stackoverflow.com/questions/11197509/ios-how-to-get-device-make-and-modelhttp://stackover ...

  3. Java中的守护线程 & 非守护线程(简介)

    Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...

  4. android开发之bitmap使用

    bitmap是android中重要的图像处理工具类,通过bitmap可以对图像进行剪切.旋转.缩放等操作,同时还可以指定格式和压缩质量保存图像文件. 一.拿到一个Bitmap对象 查看源码我们知道,B ...

  5. Android开发之隐藏Activity(活动)的标题

    隐藏一个活动的标题(如您打算向用户显示状态更新时),可以使用requestWindowFeature()方法,传递Window.FEATURE_NO_TITLE常量来控制.实现如下: protecte ...

  6. pnd_start_2

    试过才知道一点都不简单,虽然表现出的逻辑是错的,但是至少运行上是正确的.

  7. 通过开发工具发布web应用到tomcat服务器中--对于小白,大神可以忽略不看,勿喷,谢谢

    需要的工具 MyEclipse和TomCat 本人用的是MyEclipse2014和TomCat7 TomCat结构图 第一步:在MyEclipse中配置TomCat 如图所示: 第二步:创建Web项 ...

  8. yii2 验证码的使用

    @see  http://www.yiiframework.com/doc-2.0/yii-captcha-captcha.html 以下根据 MVC 模型的顺序来添加代码 1. model 层, 或 ...

  9. Linux netstat命令参数解释

    netstat –ntlp 显示 tcp 的监听端口 netstat –ntlp 显示 tcp udp 的监听端口 netstat –r 显示路由表 netstat –rn 显示路由表不做名称解析(较 ...

  10. Js判断对象是否为空,Js判断字符串是否为空

    Js判断对象是否为空,Js判断字符串是否为空,JS检查字符串是否为空字符串 >>>>>>>>>>>>>>>&g ...