题目:spoj375
题意:给定一棵n节点的树,有两种操作,①修改某一条边的权值②查询某一条链u-->v的权值最大的边。
分析:树链剖分入门学习
对树链剖分的初步认识:
①把树上的边分为两类:重边和轻边。
②任意两个节点u和v连接的这条链上,重链和轻边的数目都不超过logn(n为总的边数)
很明显,以某个轻节点为根的子树的规模不会超过父亲节点的规模的一半,否则就不是轻节点了。
1.在链u--->v上,轻边数目不超过logn
瞎证明:
lca=LCA(u,v)。
首先证明u-->lca的链上,轻边数目不超过logn。在极端情况下,链上的边全部为轻边。
由于u--->lca的这条链上以每个点为根节点的子树的大小最多为父亲节点的一半,从lca开始往下走,每次节点的数目都会最少减少一半,所以最多减少logn次就会走到底。
同理证明v--->lca的链上,轻边数目不超过logn。
所以,在链u--->v上,轻边数目不超过2*logn.....
2.在链u--->v上,重链的数目不超过logn
瞎证明:
u--->v这条链可以看成一条链有两种颜色(就是重边和轻边),重边为黑,轻边为白。//白边把黑边分成很多段
初始链全部为黑色,现在如何将链涂色,使得黑边的数目最多,明显就是让白色交错在里面,而白色的边最多logn条,所以黑色的链最多也是logn条。
③任意两个节点u和v连接的这条链上,一条重链上重边在线段树里面的编号是连续的,因此可以在线段树里面O(logn)访问。(从u到top[u]这条重链)
④任意两个节点u和v连接的这条链上,轻边在线段树里面的编号不一定连续。
⑤查询和修改的u-->v这条链的时间复杂度为logn*logn
代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int INF = 2147483647;
const int MAXN = 10010;
struct node
{
int v,next;
}List[MAXN*2];
int head[MAXN],cnt;
int top[MAXN];//top[v]表示v所在的重链的顶端节点
int fa[MAXN]; //父亲节点
int deep[MAXN];//深度
int sz[MAXN];//sz[v]表示以v为根的子树的节点数
int p[MAXN];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[MAXN];//和p数组相反
int son[MAXN];//重儿子
int pos;
void init()
{
cnt = 0;
memset(head,-1,sizeof(head));
pos = 0;
memset(son,-1,sizeof(son));memset(fa,-1,sizeof(fa));
}
void add(int u,int v)
{
List[cnt].v=v;
List[cnt].next=head[u];
head[u]=cnt++;
}
int dfs1(int cur,int dp) //第一遍dfs求出fa,deep,sz,son
{
deep[cur]=dp;
sz[cur]=1;
son[cur]=-1;
for(int i=head[cur];~i;i=List[i].next)
{
int to=List[i].v;
if(fa[cur]!=to)
{
fa[to]=cur;
sz[cur]+=dfs1(to,dp+1);
if(son[cur]==-1 || sz[to]>sz[son[cur]])
son[cur]=to;
}
}
return sz[cur];
}
void dfs2(int cur,int sp) //第二遍dfs求出top和p
{
top[cur]=sp;
p[cur]=pos++;
fp[p[cur]]=cur;
if(son[cur]==-1)
return ;
dfs2(son[cur],sp);
for(int i=head[cur];~i;i=List[i].next)
{
int to=List[i].v;
if(to!=son[cur] && fa[cur]!=to)
dfs2(to,to);
}
}
//线段树
int tree[MAXN<<4];
void update(int pos,int v,int l,int r,int rt)
{
if(l==r)
{
tree[rt]=v;
return ;
}
int m=(l+r)>>1;
if(pos<=m)
update(pos,v,lson);
else
update(pos,v,rson);
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l && r<=R)
return tree[rt];
int m=(l+r)>>1,fg1=-INF,fg2=-INF;
if(L<=m)
fg1=query(L,R,lson);
if(R>m)
fg2=query(L,R,rson);
return max(fg1,fg2);
}
int Q(int L,int R)
{
return query(L,R,1,pos,1);
}
int find(int u,int v)//查询u->v边的最大值
{
int f1 = top[u], f2 = top[v];
int tmp = 0;
while(f1 != f2)
{
if(deep[f1] < deep[f2])
{
swap(f1,f2);
swap(u,v);
}
tmp = max(tmp,Q(p[f1],p[u])); //f1在线段树里面的编号比u小
u = fa[f1]; f1 = top[u];
}
if(u == v)return tmp;
if(deep[u] > deep[v]) swap(u,v);
return max(tmp,Q(p[son[u]],p[v]));
}
int e[MAXN][3];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
int n;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
for(int i = 0;i < n-1;i++)
{
scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
add(e[i][0],e[i][1]);
add(e[i][1],e[i][0]);
}
dfs1(1,1);
dfs2(1,1);
for(int i = 0;i < n-1; i++)
{
if(deep[e[i][0]] > deep[e[i][1]])
swap(e[i][0],e[i][1]);
update(p[e[i][1]],e[i][2],1,pos,1);
}
char op[10];
int u,v;
while(scanf("%s",op) == 1)
{
if(op[0] == 'D')break;
scanf("%d%d",&u,&v);
if(op[0] == 'Q')
printf("%d\n",find(u,v));
else update(p[e[u-1][1]],v,1,pos,1);
}
}
return 0;
}