hdu 3966 最纯洁的树链剖分

40 篇文章 0 订阅
4 篇文章 0 订阅
//	hdu 3966 最纯洁的树链剖分 
	
//	题目意思就不多说啦。经典的树链剖分的裸题,看了
//	好久好久,才把树链剖分的基本思想看了一遍,然后
//	自己yy拉好重链,写了个挺丑的线段树。然后七搞八搞
//	总算是搞出来啦,仔细想来,点还是很简单的,看着
//	牛人的那篇经典的入门的讲解,很奇妙,强烈推荐哟
//	好吧,我等菜鸟继续加油哟。顺带提一下,此题区间更新
//	单点查询。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define cls(x,a) memset(x,(a),sizeof(x))
#pragma comment(linker,"/STACK:1024000000,1024000000")
using namespace std;
const int maxn = 50000 + 8;
int n,m,p;
int a[maxn];

int num;
int head[maxn];
struct edge{
	int to;
	int next;

	edge(){

	}
	edge(int to,int next):to(to),next(next){

	}

}edges[maxn<<1];


int id;
int dep[maxn]; // 深度
int father[maxn]; // 父节点
int idx[maxn]; //每个节点在线段树中的位置
int siz[maxn]; // 以该节点为根的子树节点数目
int son[maxn]; // 重儿子
int top[maxn]; // 链头
int rk[maxn]; // 与idx相反的定义,线段树中的位置在实际中的节点

void add_edges(int u,int v){
	edges[num] = edge(v,head[u]);
	head[u] = num++;
}

void dfs(int u,int fa,int d){
	dep[u] = d;
	father[u] = fa;
	siz[u] = 1;
	son[u] = 0;

	for(int i = head[u]; i != -1;i = edges[i].next){
		int v = edges[i].to;

		if (v == fa){
			continue;
		}
		dfs(v,u,d+1);
		siz[u] += siz[v];

		if (siz[son[u]] < siz[v])
			son[u] = v;
	}
	
}

void dfs(int u,int tp){
	top[u] = tp;
	idx[u] = id++;
	rk[idx[u]] = u;
	if (son[u])
		dfs(son[u],tp);

	for (int i = head[u];i!=-1;i = edges[i].next){
		int v = edges[i].to;

		if (v == father[u] || v == son[u])
			continue;

		dfs(v,v);
	}
}


// 线段树部分

int seg[maxn<<2];
int laze[maxn<<2];
int ql,qr,delta;
void push_up(int ro){
	seg[ro] = seg[ro<<1] + seg[ro<<1|1];
}

void push_down(int ro,int m){
	if (laze[ro]){
		laze[ro<<1] += laze[ro]; 
		laze[ro<<1|1] += laze[ro];
		seg[ro<<1] += laze[ro] * (m - (m>>1));
		seg[ro<<1|1] += laze[ro] * (m>>1);
		laze[ro] = 0;
	}
}

void build(int ro,int L,int R){
	laze[ro] = 0;
	if (L == R){
		seg[ro] = a[rk[L]];
		return;
	}

	int M = (L + R) >> 1;

	build(ro<<1,L,M);
	build(ro<<1|1,M+1,R);
	push_up(ro);
}

void update(int ro,int L,int R){

	if (ql<=L && R<=qr){
		laze[ro] += delta;
		seg[ro] += (R-L+1) * delta;
		return ;
	}
	push_down(ro,R-L+1);
	int M = (L + R)>>1;

	if (ql <= M)	update(ro<<1,L,M);
	if (M < qr)	update(ro<<1|1,M+1,R);
	push_up(ro);
}

int query(int ro,int L,int R){

	if (L==R){
		return seg[ro];
	}
	
	push_down(ro,R-L+1);

	int M = (L + R) >> 1;

	if (ql <= M)	return query(ro<<1,L,M);
	else	return query(ro<<1|1,M+1,R);
	
}



void modify(int u,int v,int k){
	int p = top[u],q = top[v];

	while(p!=q){
		if (dep[p] < dep[q]){
			swap(p,q);
			swap(u,v);
		}
		ql = idx[p];
		qr = idx[u];
		delta = k;
		update(1,1,n);

		u = father[p];
		p = top[u];
	}

	if (dep[u] > dep[v])
		swap(u,v);

	ql = idx[u];
	qr = idx[v];
	delta = k;
	update(1,1,n);

}

void input(){
	cls(head,-1);
	cls(seg,0);
	cls(laze,0);
	num = 0;
	id = 1;
	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);
		add_edges(u,v);
		add_edges(v,u);
	}

	dfs(1,0,0);
	dfs(1,1);

	build(1,1,n);
	for (int i=1;i<=p;i++){
		char s[2];
		scanf("%s",s);
		if (s[0]=='Q'){
			int u;
			scanf("%d",&u);
			ql = idx[u];
			printf("%d\n",query(1,1,n));
		}else if (s[0]=='I'){
			int a,b,k;
			scanf("%d%d%d",&a,&b,&k);
			modify(a,b,k);
		}else {
			int a,b,k;
			scanf("%d%d%d",&a,&b,&k);
			modify(a,b,-k);
		}
	}

}

int main(){
//	freopen("1.txt","r",stdin);
	while(scanf("%d%d%d",&n,&m,&p)!=EOF){
		input();
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值