第一道树链剖分,学习的时候参考了下面连接博主的文章,含义深入浅出,看了之后就非常清楚树链剖分的原理了:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html
个人的理解是,树链剖分实际上是在树于链的结构中找到了一种平衡,一个单纯的链在树链剖分的角度来看是一条长长的重链,在处理的时候等效于平常的区间线段树,但是当变成树路径的时候,边不具有连续性,因此很难去用线段树去维护,所以自然的,有人想出了一个办法,通过某种办法对树上的链进行编号,使得两个点的路径上的边尽可能多的连续起来,而且连续的和不连续的边加起来不超过logn条,这样我在更新线段树的时候只需要找出logn条这些边,每次logn的更新,所以每次修改树路径就是log^2n的代价.
用术语来说,重链就是连续的边,轻边就是不连续的.所以树链剖分实际上是利用logn的代价将树路径转换成了区间.所以对树路径的所有操作都转化成最基本的线段树的问题.你所要做的就是做两次dfs,分好轻重链,两边不断往上更新到lca处.链接中的博客给了一个很好的范本.
题目给的是边权,点权也是同样可以处理的,将点权放在连向父亲的那条边上(对于非根结点).对于根结点,我们可以再弄一个根的根结点(下面代码里就是0号)连一条边,最后每个点权都会对应于一条边.其中相应的代码中有些细节也是需要修改的.不过自己写的跑的微慢呀...用了2s..
#pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<vector> #include<cmath> #define maxn 50000 #define ll long long using namespace std; vector<int> G[maxn+50]; int n,m,p; int son[maxn+50],w[maxn+50],fa[maxn+50],siz[maxn+50],top[maxn+50],dep[maxn+50],totw; int edge[maxn+50]; int wht[maxn+50]; struct Node { int l,r; ll sum,add; }N[4*maxn+50]; void pushUp(int i) { N[i].sum=N[i<<1].sum+N[i<<1|1].sum; } void pushDown(int i) { if(N[i].add!=0){ if(N[i].l!=N[i].r){ int t=N[i].add; N[i<<1].sum+=(N[i<<1].r-N[i<<1].l+1)*t; N[i<<1|1].sum+=(N[i<<1|1].r-N[i<<1|1].l+1)*t; N[i<<1].add+=t; N[i<<1|1].add+=t; } N[i].add=0; } } void build(int i,int L,int R) { N[i].l=L;N[i].r=R;N[i].add=0; if(L==R){ N[i].sum=edge[L]; return; } int M=(L+R)>>1; build(i<<1,L,M); build(i<<1|1,M+1,R); pushUp(i); } void add(int i,int L,int R,int val) { if(N[i].l==L&&N[i].r==R){ N[i].sum+=(R-L+1)*val; N[i].add+=val; return; } pushDown(i); int M=(N[i].l+N[i].r)>>1; if(M>=R){ add(i<<1,L,R,val); } else if(M<L){ add(i<<1|1,L,R,val); } else{ add(i<<1,L,M,val); add(i<<1|1,M+1,R,val); } pushUp(i); } int query(int i,int L,int R) { if(N[i].l==L&&N[i].r==R){ return N[i].sum; } pushDown(i); int M=(N[i].l+N[i].r)>>1; if(M>=R){ return query(i<<1,L,R); } else if(M<=L){ return query(i<<1|1,L,R); } else{ return query(i<<1,L,M)+query(i<<1|1,M+1,R); } pushUp(i); } void dfs1(int v) { siz[v]=1;son[v]=0;int msize=0; for(int i=0;i<G[v].size();++i){ int u=G[v][i]; if(u==fa[v]) continue; fa[u]=v; dep[u]=dep[v]+1; dfs1(u); if(siz[u]>msize) {son[v]=u;msize=siz[u];} siz[v]+=siz[u]; } } void dfs2(int v,int tp) { w[v]=++totw;top[v]=tp;edge[w[v]]=wht[v]; if(son[v]!=0) dfs2(son[v],top[v]); for(int i=0;i<G[v].size();++i){ int u=G[v][i]; if(u!=fa[v]&&u!=son[v]){ dfs2(u,u); } } } void initEdge() { fa[0]=totw=dep[0]=0; dfs1(0);dfs2(0,0); build(1,1,n+1); } ll query_path(int va,int vb) { ll res=0; int f1=top[va],f2=top[vb]; while(f1!=f2) { if(dep[f1]<dep[f2]){ swap(f1,f2);swap(va,vb); } res+=(ll)query(1,w[f1],w[va]); va=fa[f1];f1=top[va]; } if(va==vb) return res+query(1,w[va],w[vb]); if(dep[va]>dep[vb]) swap(va,vb); res+=(ll)query(1,w[va],w[vb]); return res; } void add_path(int va,int vb,int val) { int f1=top[va],f2=top[vb]; while(f1!=f2) { if(dep[f1]<dep[f2]){ swap(f1,f2);swap(va,vb); } add(1,w[f1],w[va],val); va=fa[f1];f1=top[va]; } if(va==vb) {add(1,w[va],w[va],val);return;} if(dep[va]>dep[vb]) swap(va,vb); add(1,w[va],w[vb],val); } int main() { while(cin>>n>>m>>p) { for(int i=1;i<=n;++i){ scanf("%d",&wht[i]); G[i].clear(); } int u,v; for(int i=0;i<n-1;++i){ scanf("%d%d",&u,&v); G[u].push_back(v);G[v].push_back(u); } G[0].push_back(1); initEdge(); char s[3];int val; for(int i=0;i<p;++i){ scanf("%s",&s); if(s[0]=='I'||s[0]=='D'){ scanf("%d%d%d",&u,&v,&val); if(s[0]=='I'){ add_path(u,v,val); } else{ add_path(u,v,-val); } } else{ scanf("%d",&u); printf("%d ",query_path(u,u)); } } } return 0; }