解析:
树链剖分的模板题,再套上一个线段树模板就可以了。
这里需要注意的是每找到一条链的过程中,这条链的点的在线段树中的编号是连续的。
就是第一条重链有n个点,编号就是1,...n
第二条重链有n-k个点,编号就是n+1...2n-k
以此类推,这样按照这些编号插入到线段树就可以了。
这些处理做完之后,就可以一一查询了
还有就是这道题我用vector存边就TLE了,用数组存就过了,不知道是我哪写错了还是这两种方式的问题
树链剖分大神讲解:点击打开链接
另一个讲了树面剖分的思想以及复杂度 点击打开链接 点击打开链接
以下内容属于摘录:
其实树链剖分就是把边哈希到线段树上的数据结构。
性质:从根到某一点的路径上轻边、重边的个数都不大于logn。
所以这样查找的时间复杂度相当于log2(n)
树链剖分可以解决很多问题,辅助一些线段树之类的数据结构可以解决一些树上修改的问题。还可以求LCA,不过复杂度比RMQ实现的LCA多一个log。
证明如下:
(1)
由于任一轻儿子对应的子树大小要小于父节点所对应子树大小的一半
因此从一个轻儿子沿轻边向上走到父节点后 所对应的子树大小至少变为两倍以上
经过的轻边条数自然是不超过log2Nlog2N的
然后由于重链都是间断的 ((连续的可以合成一条))
所以经过的重链的条数是不超过轻边条数+1+1的
因此经过重链的条数也是loglog级别的
综合可知原命题得证(这个证明zz表示看不懂)
(2)点击打开链接
每当学习了一个新算法,我们最关切的就是它的时间复杂度了。
如果树链剖分的时间复杂度高到爆,我们写的时候估计得虚死。
但是可以证明,树链剖分后的树,从根节点到任意一个叶节点的路径只会与O(logn)O(logn)条树链相交。这意味着将两个节点逼近到同一条链上时,只需经过O(logn)O(logn)次跳转。
即基于树剖的其它操作的时间复杂度为Ω(logn)Ω(logn)(注意是下界,具体的上界取决于操作本身的附加的复杂度)。
证明思路因该是这样的:
可以考虑一棵树中轻边的数量。由于从某一个节点开始,每走一条轻边,子树的大小都会减小一倍。因此任意一条树链上只有O(logn)O(logn)条轻边,即意味着只有O(logn)O(logn)。
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 50000 + 100;
#define INF 1147483647
#define M(a) memset(a,0,sizeof(a))
int val[MAXN],fa[MAXN],dep[MAXN],siz[MAXN],id[MAXN],top[MAXN],ran[MAXN],son[MAXN];
typedef struct ee
{
int u,v;
int next;
}ee;
ee edge[MAXN*2];
int head[MAXN],cnt;
int num,n;
typedef struct node
{
int ii;
int maxx;
int sum;
}node;
node Btree[MAXN*4];
void addedge(int u,int v)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void dfs1(int x,int f,int d)
{
dep[x]=d;
siz[x]=1;
son[x]=0;
fa[x]=f;
for(int i=head[x];i!=-1;i=edge[i].next)
{
int tmp=edge[i].v;
if(tmp==f) continue;
dfs1(tmp,x,d+1);
siz[x]+=siz[tmp];
if(siz[son[x]]<siz[tmp])
{
son[x]=tmp;
}
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
id[x]=++num;
ran[num]=x;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i!=-1;i=edge[i].next)
{
int tmp=edge[i].v;
if(tmp==fa[x]||tmp==son[x]) continue;
dfs2(tmp,tmp);
}
}
void build(int stu[],int l,int r,int root)
{
if(l>r) return;
if(l==r)
{
Btree[root].ii=l;
Btree[root].maxx=stu[ran[l]];
Btree[root].sum=stu[ran[l]];
return;
}
int mid=(l+r)/2;
build(stu,l,mid,root*2);
build(stu,mid+1,r,root*2+1);
Btree[root].maxx=max(Btree[root*2].maxx,Btree[root*2+1].maxx);
Btree[root].sum=Btree[root*2].sum+Btree[root*2+1].sum;
}
void update(int root,int l,int r,int index,int val)
{
if(l>r) return;
if(l==r)
{
if(l==index)
{
Btree[root].maxx=Btree[root].sum=val;
}
return;
}
int mid=(l+r)/2;
if(index<=mid)
update(root*2,l,mid,index,val);
else
update(root*2+1,mid+1,r,index,val);
Btree[root].maxx=max(Btree[2*root].maxx,Btree[2*root+1].maxx);
Btree[root].sum=Btree[2*root].sum+Btree[2*root+1].sum;
}
int querymax(int root,int l,int r,int s,int e)
{
if(r<s||l>e)
{
return -INF;
}
if(l>r) return -INF;
if(s<=l&&r<=e)
{
return Btree[root].maxx;
}
int mid=(l+r)/2;
return max(querymax(2*root,l,mid,s,e),querymax(root*2+1,mid+1,r,s,e));
}
int querysum(int root,int l,int r,int s,int e)
{
if(r<s||l>e)
{
return 0;
}
if(l>r) return 0;
if(s<=l&&r<=e)
{
return Btree[root].sum;
}
int mid=(l+r)/2;
return (querysum(2*root,l,mid,s,e)+querysum(root*2+1,mid+1,r,s,e));
}
void solvemax(int a,int b)
{
if(dep[top[a]]<dep[top[b]])
{
swap(a,b);
}
int ans=-INF;
while(top[a]!=top[b])
{
//int tmp=top[a];
ans=max(ans,querymax(1,1,n,id[top[a]],id[a]));
a=fa[top[a]];
if(dep[top[a]]<dep[top[b]])
{
swap(a,b);
}
}
if(id[a]>id[b]) swap(a,b);
ans=max(ans,querymax(1,1,n,id[a],id[b]));
printf("%d\n",ans);
}
void solvesum(int a,int b)
{
if(dep[top[a]]<dep[top[b]])
{
swap(a,b);
}
int ans=0;
while(top[a]!=top[b])
{
//int tmp=top[a];
ans=ans+querysum(1,1,n,id[top[a]],id[a]);
a=fa[top[a]];
if(dep[top[a]]<dep[top[b]])
{
swap(a,b);
}
}
if(id[a]>id[b]) swap(a,b);
ans+=querysum(1,1,n,id[a],id[b]);
printf("%d\n",ans);
}
int main()
{
int q;
while(scanf("%d%d",&n,&q)!=EOF)
{
num=0;
M(Btree);
M(son);
cnt=0;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
}
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs1(1,0,1);
dfs2(1,1);
int mode,a,b;
build(val,1,n,1);
for(int i=0;i<q;i++)
{
scanf("%d%d%d",&mode,&a,&b);
if(mode==0)
{
solvemax(a,b);
}
else if(mode==1)
{
solvesum(a,b);
}
else
{
update(1,1,n,id[a],b);
}
}
}
return 0;
}