hdu 3966 Aragorn's Story

        题意:一棵树,每个节点有初始权值,有p次操作,要么询问某个点的权值,要么修改树上两个节点u-v间路径的权值。

        思路:树链剖分。这是我做的第一道树链剖分题,我对树链剖分的理解是,把树递归地分割成线性(重边优先),按顺序排列起来,最后长度和树的节点数一样。然后就可以利用线段树,对树上的路径进行维护。


#pragma comment(linker, "/STACK:1024000000,1024000000")  
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<string.h>
#include<cstdio>

using namespace std;

#define maxn 50010

int a[maxn];

int head[maxn];
int next[maxn*2];
int to[maxn*2];

int siz[maxn];	//以i为根的子树的节点数
int dep[maxn];	//节点深度
int top[maxn];	//所在链的顶端节点
int fa[maxn];	//父节点
int son[maxn];	//重儿子
int w[maxn];	//节点在线段树中的位置
int w_1[maxn];
bool vis[maxn];
int pos;
int end;

int n,m,p;

void init(int n){
	for(int i=1;i<=n;i++){
		siz[i]=top[i]=son[i]=0;
		dep[i]=w[i]=w_1[i]=fa[i]=0;
		head[i]=-1;
	}
	pos=0;
	end=0;
}

void addedge(int u,int v){
	to[end]=v,next[end]=head[u],head[u]=end++;  
    to[end]=u,next[end]=head[v],head[v]=end++;  
}

void dfs(int x,int pre){
	vis[x]=1;
	dep[x]=dep[pre]+1;
	fa[x]=pre;
	for(int i=head[x];i!=-1;i=next[i]){
		if(to[i]==pre)continue;
		if(vis[to[i]])continue;
		dfs(to[i],x);
		siz[x]+=siz[to[i]];
		if(son[x]==0)son[x]=to[i];
		else if(siz[son[x]]<siz[to[i]])son[x]=to[i];
	}
	siz[x]++;
}

void dfs2(int x,int pre){
	vis[x]=1;
	pos++;
	w[x]=pos;
	w_1[pos]=x;
	if(son[fa[x]]==x)top[x]=top[fa[x]];
	else top[x]=x;
	//先递归重儿子 
	if(son[x]==0)return;
	dfs2(son[x],x);
	for(int i=head[x];i!=-1;i=next[i]){
		if(to[i]==pre)continue;
		if(to[i]==son[x])continue;
		if(vis[to[i]])continue;
		dfs2(to[i],x);
	}
}

struct node{
	int l,r;
	int val;
	int lazy;
};
node tree[maxn<<2];

void build_tree(int n,int l,int r){
	tree[n].l=l; tree[n].r=r;
	tree[n].val=tree[n].lazy=0;
	if(l==r){
		tree[n].val=a[w_1[l]];
		return;
	}
	
	int mid=(l+r)>>1;
	build_tree(n<<1,l,mid);
	build_tree((n<<1)|1,mid+1,r);
}

void push_down(int x){
	int& add=tree[x].lazy;
	node& lch=tree[x<<1];
	node& rch=tree[(x<<1)|1];
	
	if(lch.l==lch.r)lch.val+=add;
	else lch.lazy+=add;
	if(rch.l==rch.r)rch.val+=add;
	else rch.lazy+=add;
	add=0;
}


void update(int n,int l,int r,int v){
	if(tree[n].l==l&&tree[n].r==r){
		if(l==r){
			tree[n].val+=v;	
		}else{
			tree[n].lazy+=v;
		}
		return;
	}
	if(tree[n].lazy)push_down(n);
	
	int mid=(tree[n].l+tree[n].r)>>1;
	if(r<=mid){
		update(n<<1,l,r,v);
	}else{
		if(l>mid){
			update((n<<1)|1,l,r,v);
		}else{
			update(n<<1,l,mid,v);
			update((n<<1)|1,mid+1,r,v);
		}
	}
}

int query(int n,int pos){
	if(tree[n].l==tree[n].r)return tree[n].val;
	if(tree[n].lazy)push_down(n);
	int mid=(tree[n].l+tree[n].r)>>1;
	if(pos<=mid) return query(n<<1,pos);
	else return query((n<<1)|1,pos);
}

int main(){
	while(cin>>n>>m>>p){
		init(n);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		for(int i=1;i<=m;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			addedge(u,v);
		}
		
		memset(vis,0,sizeof(vis));
		dfs(1,0);
		memset(vis,0,sizeof(vis));
		dfs2(1,0);
		
		build_tree(1,1,n);
		
		char op[2];
		for(int i=1;i<=p;i++){	
			scanf("%s",op);
			if(op[0]=='Q'){
				int c;
				scanf("%d",&c);
				printf("%d\n",query(1,w[c]));
			}else{
				int u,v,k;
				scanf("%d%d%d",&u,&v,&k);
				if(op[0]=='D')k=-k;
				
				//不在一条路径上,上爬 
				while(top[u]!=top[v]){//
					if(dep[top[u]]<dep[top[v]])swap(u,v);
					update(1,w[top[u]],w[u],k);
					u=fa[top[u]];
				}
				//在一条路径上 
				if(w[u]>w[v])swap(u,v);
				update(1,w[u],w[v],k);
				
			}
		}
	}
	return 0;
}


你可能感兴趣的:(树链剖分)