BZOJ 1036-树的统计Count(树链刨分)

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 17520   Solved: 7150
[ Submit][ Status][ Discuss]

Description

  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

  输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

  对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16

HINT


做的第一道树链刨分题,这也是一道树链刨分经典题,呢什么是树链刨分呢

参考博客:树链刨分详解

树链,即树上的路径,现在我们的任务是所谓的剖分。所以我们可以看出,树链剖分并不是一种单独的数据结构,

不像堆,线段树等等,而是直接在一棵普通的树上处理,然而单是这一课树是并没有什么卵用的。今天先讲一个

相对比较简单的情况——用一棵线段树维护主树每条边的权值

常见的路径剖分的方法是轻重树链剖分(启发式剖分)
将树中的边分为:轻边和重边 ž定义size(X)为以X为根的子树的节点个数。 ž令V为U的儿子节点中size值最大的节点,那么边(U,V)被称为重边,          树中重边之外的边被称为轻边。
性质:ž轻边(U,V),size(V)<=size(U)/2。 ž从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径。
具体步骤如下:
第一遍dfs 求出树每个结点的深度deep[x],其为根的子树大小size[x], 以及祖先的信息fa[x][i]表示x往上距离为2^i的祖先,具体代码如下:
void dfs1(int u,int p)
{
	int i;
	fa[u]=p;
	dep[u]=dep[p]+1;
	for(i=0;i<q[u].size();i++)
	{
		int v=q[u][i];
		if(v==p)
			continue;
		dfs1(v,u);
		siz[u]+=siz[v]+1;
		if(son[u]==0 || siz[v]>siz[son[u]])
			son[u]=v;
	}
}

第二遍dfs
ž根节点为起点,向下拓展构建重链
选择最大的一个子树的根继承当前重链
其余节点,都以该节点为起点向下重新拉一条重链
ž给每个结点分配一个位置编号,每条重链就相当于一段区间,用数据结构去维护。
把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可
void dfs2(int u,int p)
{
	int i;
	top[u]=p;
	rak[u]=++cnt;
	id[cnt]=u;
	if(son[u]==0)
		return;
	dfs2(son[u],p);
	for(i=0;i<q[u].size();i++)
	{
		int v=q[u][i];
		if(v==son[u] || v==fa[u])
			continue;
		dfs2(v,v);
	}
}
修改操作
1、单独修改一个点的权值
根据其编号直接在数据结构中修改就行了。
2、修改点u和点v的路径上的权值
(1)若u和v在同一条重链上
直接用数据结构修改pos[u]至pos[v]间的值。
(2)若u和v不在同一条重链上
一边进行修改,一边将u和v往同一条重链上靠,然后就变成了情况(1)。
总代码:
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<math.h>
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef long long  ll;
#define inf 1000000000
#define mod 1000000007 
#define  maxn  200005
#define  lowbit(x) (x&-x)
#define  eps 1e-10
vector<int>q[maxn];
int dep[maxn],son[maxn],top[maxn],n,id[maxn];
int rak[maxn],fa[maxn],a[maxn],cnt,siz[maxn];
struct node
{
	int maxs,sum;
}tr[maxn];
void dfs1(int u,int p)
{
	int i;
	fa[u]=p;
	dep[u]=dep[p]+1;
	for(i=0;i<q[u].size();i++)
	{
		int v=q[u][i];
		if(v==p)
			continue;
		dfs1(v,u);
		siz[u]+=siz[v]+1;
		if(son[u]==0 || siz[v]>siz[son[u]])
			son[u]=v;
	}
}
void dfs2(int u,int p)
{
	int i;
	top[u]=p;
	rak[u]=++cnt;
	id[cnt]=u;
	if(son[u]==0)
		return;
	dfs2(son[u],p);
	for(i=0;i<q[u].size();i++)
	{
		int v=q[u][i];
		if(v==son[u] || v==fa[u])
			continue;
		dfs2(v,v);
	}
}
void build(int x,int l,int r)
{
	if(l==r)
	{
		tr[x].maxs=a[id[l]];
		tr[x].sum=a[id[l]];
		return;
	}
	int m=(l+r)/2;
	build(x*2,l,m);
	build(x*2+1,m+1,r);
	tr[x].sum=tr[x*2].sum+tr[x*2+1].sum;
	tr[x].maxs=max(tr[x*2].maxs,tr[x*2+1].maxs);
}
void updata(int id,int l,int r,int x,int y)
{
	if(l==r && l==x)
	{
		tr[id].maxs=y;
		tr[id].sum=y;
		return;
	}
	int m=(l+r)/2;
	if(x<=m)
		updata(id*2,l,m,x,y);
	else
		updata(id*2+1,m+1,r,x,y);
	tr[id].sum=tr[id*2].sum+tr[id*2+1].sum;
	tr[id].maxs=max(tr[id*2].maxs,tr[id*2+1].maxs);
}
int querym(int x,int l,int r,int L,int R)
{
	if(l>=L && r<=R)
		return tr[x].maxs;
	int m=(l+r)/2,res=-inf;
	if(L<=m)
		res=max(res,querym(x*2,l,m,L,R));
	if(R>m)
		res=max(res,querym(x*2+1,m+1,r,L,R));
	return res;
}
int trquerym(int x,int y)
{
	int t1,t2,now=-inf;
	t1=top[x];t2=top[y];
	while(t1!=t2)
	{
		if(dep[t1]<dep[t2])
			swap(t1,t2),swap(x,y);
		now=max(now,querym(1,1,n,rak[t1],rak[x]));
		x=fa[t1];t1=top[x];
	}
	if(dep[x]>dep[y])
		swap(x,y);
	now=max(now,querym(1,1,n,rak[x],rak[y]));
	return now;
}
int querys(int x,int l,int r,int L,int R)
{
	if(l>=L && r<=R)
		return tr[x].sum;
	int m=(l+r)/2,res=0;
	if(L<=m)
		res+=querys(x*2,l,m,L,R);
	if(R>m)
		res+=querys(x*2+1,m+1,r,L,R);
	return res;
}
int trquerys(int x,int y)
{
	int t1,t2,sum=0;
	t1=top[x];t2=top[y];
	while(t1!=t2)
	{
		if(dep[t1]<dep[t2])
			swap(t1,t2),swap(x,y);
		sum+=querys(1,1,n,rak[t1],rak[x]);
		x=fa[t1];t1=top[x];
	}
	if(dep[x]>dep[y])
		swap(x,y);
	sum+=querys(1,1,n,rak[x],rak[y]);
	return sum;
}
int  main(void)
{
	char s[20];
	int i,j,x,y,m;
	scanf("%d",&n);
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		q[x].push_back(y);
		q[y].push_back(x);
	}
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	dfs1(1,0);dfs2(1,1);
	build(1,1,n);
	scanf("%d",&m);
	while(m--)
	{
		scanf("%s%d%d",s,&x,&y);
		if(s[0]=='C')
			updata(1,1,n,rak[x],y);
		else if(s[1]=='M')
			printf("%d\n",trquerym(x,y));
		else
			printf("%d\n",trquerys(x,y));
	}
	return 0;
}






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值