HDU3966 Aragorn's Story 树链剖分

前端之家收集整理的这篇文章主要介绍了HDU3966 Aragorn's Story 树链剖分前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

Problem Address:http://acm.hdu.edu.cn/showproblem.php?pid=3966


【前言】


前几天在学树链剖分。主要还是为比赛做一下模板。但是找不到很基础的题可以做。

这道题的线段树部分不是最简单的那种,用的也是别人的模板。

上次WA了,好几天还没碰。

但是今天早上起来看了一下,就改了一个地方,然后就过了。

看来应了那句话:把最困难的任务放在早上做。


【思路】


简单说下树链剖分。

树链剖分的应用就是动态地修改一棵树上的边权,同时动态地查询每两个节点之间路径上的信息,包括最值、和等。

树链剖分就是把一棵树从上到下划分为一条条链,这样做的好处是更新或查询的时候可以成段地进行。

详细请看:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html

网上代码很多,讲解的很少。上面这篇文章我觉得里面讲的很详细,例子给的也很到位。

当看懂之后发现,更新或查询的时候就是使用线段树了!

线段树的应用包括求最值、和等。也包括成段的更新和查询


代码


树链剖分和线段树代码都是用的别人的,不过其实树链剖分没有想象中的那么困难。


#include <iostream>
using namespace std;

const int maxn = 50010;

struct edgeNode
{
	int value;
	int to;
	int next; 
}edge[maxn*2];
int ect;
int ehead[maxn];
int eroot;

int tct;

int dep[maxn],w[maxn],fa[maxn],top[maxn],son[maxn],siz[maxn];

void initEdge(int n)//树的初始化
{
	ect = 0;
	eroot = 1;
	int i;
	for (i=0; i<=n; i++) ehead[i] = -1;
}

void insertEdge(int from,int to,int value)//插入一条树的边
{
	edge[ect].to = to;
	edge[ect].value = value;
	edge[ect].next = ehead[from];
	ehead[from] = ect;
	ect++;
}

void dfs1(int v)//第一层搜索
{
     siz[v] = 1; 
	 son[v] = 0;
	 int i;
     for (i=ehead[v]; i+1!=0; i=edge[i].next)
	 {
         if (edge[i].to!=fa[v])
         {
             fa[edge[i].to] = v;
             dep[edge[i].to] = dep[v]+1;
             dfs1(edge[i].to);
             if (siz[edge[i].to]>siz[son[v]]) son[v]=edge[i].to;
             siz[v] += siz[edge[i].to];
         }
	 }
}

void dfs2(int v,int tp)//第二层搜索,获得每个节点在线段树中的位置
{
	tct++;
	w[v] = tct;
	top[v] = tp;
	if (son[v]!=0) dfs2(son[v],top[v]);
	int i;
	for (i=ehead[v]; i+1!=0; i=edge[i].next)
	{
		if (edge[i].to!=son[v] && edge[i].to!=fa[v])
			dfs2(edge[i].to,edge[i].to);
	}
}

int enemy[maxn];
//以下为线段树部分
#define lson l,m,rt << 1
#define rson m + 1,r,rt << 1 | 1
#define LL int

LL add[maxn<<2];
LL sum[maxn<<2];

void PushUp(int rt) 
{
	sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void PushDown(int rt,int m) 
{
	if (add[rt]) {
		add[rt<<1] += add[rt];
		add[rt<<1|1] += add[rt];
		sum[rt<<1] += add[rt] * (m - (m >> 1));
		sum[rt<<1|1] += add[rt] * (m >> 1);
		add[rt] = 0;
	}
}

void buildSegment(int l,int r,int rt) //建立线段树,可以直接全部初始化为0
{
	memset(add,sizeof(add));
	memset(sum,sizeof(sum));
	/*
	add[rt] = 0;
	if (l==r)
	{
		sum[rt] = 0;
		return;
	}
	int m = (l + r) >> 1;
	buildSegment(lson);
	buildSegment(rson);
	PushUp(rt);*/
}

void updateSegment(int L,int R,int c,int l,int rt)//线段树区间更新
{
	if (L<=l && r<=R) 
	{
		add[rt] += c;
		sum[rt] += (LL)c * (r - l + 1);
		return;
	}
	PushDown(rt,r - l + 1);
	int m = (l + r) >> 1;
	if (L<=m) updateSegment(L,R,c,lson);
	if (m<R) updateSegment(L,rson);
	PushUp(rt);
}

LL querySum(int L,int rt)//线段树区间求和,不过这个不是树链剖分的区间求和
{
	if (L <= l && r <= R) 
	{
		return sum[rt];
	}
	PushDown(rt,r - l + 1);
	int m = (l + r) >> 1;
	LL ret = 0;
	if (L<=m) ret += querySum(L,lson);
	if (m<R) ret += querySum(L,rson);
	return ret;
}

void init(int n)//所有初始化
{
	eroot = (n + 1) / 2;
	fa[eroot] = tct = dep[eroot] = 0;
	memset(siz,sizeof(siz));
	dfs1(eroot);
	dfs2(eroot,eroot);
	buildSegment(1,tct,1);//建立空的线段树
	int i;
	for (i=1; i<=n; i++)//把初始的权值更新到线段树上
	{
		updateSegment(w[i],w[i],enemy[i],1,1);
	}
}
void update(int va,int vb,int c)//树链剖分的更新
{
	int f1 = top[va],f2 = top[vb];
	while(f1!=f2)
	{
		if (dep[f1]<dep[f2])
		{
			swap(f1,f2);
			swap(va,vb);
		}
		updateSegment(w[f1],w[va],1);
		va = fa[f1];
		f1 = top[va];
	}
//	if (va==vb) return;//因为这道题是以点位权的,所以点需要更新
	if (dep[va]>dep[vb])
	{
		swap(va,vb);
	}
	updateSegment(w[va],w[vb],1);
}

int main()
{
	int n,p;
	int i;
	int x,y,z;
	char cmd[5];
	while(scanf("%d %d %d",&n,&m,&p)!=EOF)
	{
		for (i=1; i<=n; i++) scanf("%d",&enemy[i]);
		initEdge(n);
		for (i=1; i<n; i++)
		{
			scanf("%d %d",&x,&y);
			insertEdge(x,0);
			insertEdge(y,x,0);
		}
		init(n);
		for (i=0; i<p; i++)
		{
			scanf("%s",cmd);
			if (cmd[0]=='I')
			{
				scanf("%d %d %d",&y,&z);
				update(x,z);
			}
			else if (cmd[0]=='D')
			{
				scanf("%d %d %d",-z);
			}
			else
			{
				scanf("%d",&x);
				printf("%d\n",querySum(w[x],w[x],1));
			}			
		}
	}
    return 0;
}

猜你在找的VB相关文章