昨天晚上脑子一抽,冒出来一个想法:“我要学树剖!”然后,今天下午+晚上就砸给了树剖….讲真,NOIP之前学省选内容并不好…我这是在作死…..——前言。
树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组、SBT、SPLAY、线段树等)来维护每一条链。(来自度娘百科)
一般是用线段树来维护吧….看上去代码很长,其实主要部分是线段树。好像线段树也是省选内容呢= =,mdzz反正我们LOI基本上都会。
怎么把这棵树剖开呢,我们需要两个DFS。
第一个DFS处理出每个点的深度deep,爹fa,以该点为根的子树大小siz,和每个点的重儿子,我们规定对于一个点t,他的儿子中siz最大的是他的重儿子
重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
轻儿子:v的其它子节点。
重边:点v与其重儿子的连边。
轻边:点v与其轻儿子的连边。
重链:由重边连成的路径。
轻链:轻边。
丢一点概念。
在第二个DFS里,主要是搞出top数组,top[i]为i所在的这条链的最顶端的元素,
我们还需要一个数组xds记录树上的每条边(或者点,视题意而定)在线段树中的位置。
1.对于节点v,当son[v]存在(即v不是叶子节点)时,显然有top[son[v]] = top[v]。线段树中,v的重边应当在v的父边的后面,记xds[son[v]] = totw+1,totw表示最后加入的一条边在线段树中的位置。此时,为了使一条重链各边在线段树中连续分布,应当进行dfs_2(son[v]);
⒉对于v的各个轻儿子u,显然有top[u] = u,并且xds[u] = totw+1,进行dfs_2过程。这就求出了top和w。(搬运)
我知道你们看不懂我在说什么….戳这里—>hahah
那接下来就是例题了。
SPOJ QTREE
戳我
题目大意:就是一个裸的树剖
输入:输入一个t,代表有t组数据
对于每组数据,第一行有个n<=10000代表有多少个点,
接下来n-1行,每行有3个数a,b,c代表从a到b的这条边权值为c
接下来每行输入一个字符串和两个整数a,b,
分别对应操作,
如果字符串是DONE就结束
字符串是CHANGE就把边a的权值改为b
字符串是QUERY就询问从a点到b点路径上的最大权值。
输出:对于每个QUERY对应一个输出
这个题是维护边的,我们需要把每一条边的权值下放到连接它的两个点中深度较大的点(根节点是没有爹的)
我们在xds数组中记录的值,xds[i]就代表第i条边在xds中的位置了。(当然,这里的第一条边就是代表根节点和他父亲连的边,但是他没有父亲,所以第一条边并没有卵用)
在进入询问之前,我们要预处理一下,
我们需要把每一条边的权值下放到连接它的两个点中深度较大的点
所以,我们对于每一条边,连接它将深度较大的点在线段树中的值修改为这条边的权值。
只用线段树维护最大值,并且是单边修改,线段树的修改部分非常好写。只要找到这个点在线段树中的位置直接修改就行了。
这个题的关键地方,是在询问最值。
图中的较粗的边是重边,蓝色数字代表每条边在线段树中的位置。
看看这张图,再想想DFS2,每条重链上的边的序号一定是连续的
如果我们要询问x到y路径上的最值。
假设a是上面的11节点,b是上面的14节点。
那么令fax==top[a]==2,fay==top[10]==10;
这时,很明显deep[fax]< deep[fay],我们对fay进行操作显然更好。
所以将fax,fay。x,y分别swap
再求fax到x的最大距离。
因为x到fax,他们的边在线段树上是一段连续的区间,所以就等同于线段树区间最值查询。
然后更新x和fax,直到fax==fay,此时x和y一定在同一条链上了。
此时,如果deep[x]>deep[y],我们将他们swap一下。
因为他们在同一条链上,所以求一遍xds[x]+1到xds[y]的最值就好了
为什么是xds[x]+1呢?
之前也说了xds[x],代表他到他爹的这条边。
显然,在这里是用不到的。
至此结束。
代码贼TM长…..
#include<algorithm>
#include<cstring>
#include<cstdio>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int maxn=50000;
struct Edge
{
int to;
int next;
}edge[maxn];
int tot;
int head[maxn];
void add(int f,int t)
{
edge[++tot]=(Edge){t,head[f]};
head[f]=tot;
}
int deep[maxn],fa[maxn],siz[maxn],son[maxn];
void dfs1(int f,int t)
{
deep[t]=deep[f]+1;
fa[t]=f;
siz[t]=1;
for(int i=head[t];i;i=edge[i].next)//神TMt打成了f
{
Edge e=edge[i];
if(e.to==f)
continue;
dfs1(t,e.to);
siz[t]+=siz[e.to];
if(siz[e.to]>siz[son[t]])
son[t]=e.to;
}
}
int top[maxn],xds[maxn];
int ttot;
void dfs2(int u,int topu)
{
top[u]=topu;
xds[u]=++ttot;
if(!son[u])
return;
dfs2(son[u],topu);
for(int i=head[u];i;i=edge[i].next)
{
Edge e=edge[i];
if(e.to==fa[u]||e.to==son[u])
continue;
dfs2(e.to,e.to);
}
}
struct Tree
{
int l,r;
int maxx;
}t[maxn<<3];
void up(int p)
{
t[p].maxx=max(t[p<<1].maxx,t[p<<1|1].maxx);
}
void expand(int p,int l,int r)
{
t[p].l=l;
t[p].r=r;
if(t[p].l==t[p].r)
{
t[p].maxx=0;
return;
}
int mid=(l+r)>>1;
expand(p<<1,l,mid);
expand(p<<1|1,mid+1,r);
up(p);
}
void change(int p,int pos,int v)
{
if(t[p].l==t[p].r)
{
t[p].maxx=v;
return;
}
int mid=(t[p].l+t[p].r)>>1;
if(pos<=mid)
change(p<<1,pos,v);
else
change(p<<1|1,pos,v);
up(p);
}
int ask_max(int p,int l,int r)
{
int ans=0;
if(l<=t[p].l&&t[p].r<=r)
{
return t[p].maxx;
}
int mid=(t[p].r+t[p].l)>>1;
if(l<=mid)
ans=max(ans,ask_max(p<<1,l,r));
if(mid+1<=r)
ans=max(ans,ask_max(p<<1|1,l,r));
return ans;
}
int find(int x,int y)
{
int fax=top[x];
int fay=top[y];
int ans=0;
while(fax!=fay)
{
if(deep[fax]<deep[fay])
{
swap(fax,fay);
swap(x,y);
}
ans=max(ans,ask_max(1,xds[fax],xds[x]));
x=fa[fax];//这一行打成了x=fax
fax=top[x];
}
if(deep[x]>deep[y])
swap(x,y);
if(x!=y)
{
ans=max(ans,ask_max(1,xds[x]+1,xds[y]));
}
return ans;
}
int ff[maxn],tt[maxn],dd[maxn];
void clr()
{
mem(ff);
mem(tt);
mem(dd);
mem(edge);
mem(top);
mem(xds);
mem(t);
mem(fa);
mem(son);
mem(head);
mem(deep);
mem(siz);
tot=0;
ttot=0;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
clr();
int n;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&ff[i],&tt[i],&dd[i]);
add(ff[i],tt[i]);
add(tt[i],ff[i]);
}
dfs1(0,1);
dfs2(1,1);
expand(1,1,n);
char hah[233];
for(int i=1;i<n;i++)
{
if(deep[ff[i]]>deep[tt[i]])
swap(ff[i],tt[i]);
change(1,xds[tt[i]],dd[i]);
}
while(1)
{
scanf("%s",hah);
if(hah[0]=='D')
break;
else
{
int a,b;
scanf("%d%d",&a,&b);
if(hah[0]=='C')
change(1,xds[tt[a]],b);
else
printf("%d\n",find(a,b));
}
}
}
return 0;
}