HDU 3966 Aragorn's Story(树链剖分,区间修改,单点查询)

本文介绍了一种利用线段树进行区间修改和单点查询的数据结构优化方法,并结合树链剖分技术实现高效的树形结构操作。通过具体的C++代码示例,详细解释了如何构建和维护树状结构的线段树,以及如何处理区间更新和查询等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

区间修改,单点查询,很容易想到线段树,树形结构的线段树可以用树链剖分。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 50005
int val_pre[maxn],val[maxn],siz[maxn],son[maxn],id[maxn],fa[maxn];
int top[maxn],dep[maxn];
int topw,m,n,p;
vector<int> G[maxn];
void dfs_1(int u,int f,int d)
{
	siz[u]=1;					//当前节点的子节点数目初始化为1 
	fa[u]=f;					//父节点 
	dep[u]=d;					//当前节点的深度 
	son[u]=0;					//最重的儿子 
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==f) continue;
		dfs_1(v,u,d+1);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v])son[u]=v;
	}
}
void dfs_2(int u,int tp)
{
	top[u]=tp;					//当前节点的所在重链的链手编号 
	id[u]=++topw;				//重新编号 
	if(son[u]) dfs_2(son[u],tp);
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa[u]||v==son[u])continue;
		dfs_2(v,v);
	}
}

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int add[maxn<<2];

void push_down(int rt)				//更新lazy标记 
{
	if(add[rt])
	{
		add[rt<<1]+=add[rt];
		add[rt<<1|1]+=add[rt];
		add[rt]=0;
	}
}
void build(int l,int r,int rt)		//建树 
{
	add[rt]=0;
	if(l==r)
	{
		add[rt]=val[r];
		return ;
	}
	int m=(l+r)>>1;
	build(lson);
	build(rson);
}
void update(int L,int R,int c,int l,int r,int rt)
{
	if(L<=l&&R>=r)
	{
		add[rt]+=c;
		return ;
	}
	push_down(rt);
	int m=(l+r)>>1;
	if(L<=m)update(L,R,c,lson);
	if(R>m)update(L,R,c,rson);
}
int query(int x,int l,int r,int rt)
{
	if(l==r)
	return add[rt];
	push_down(rt);		//查找过程中要更新lazy标记 
	int m=(l+r)>>1;
	if(x<=m)query(x,lson);
	else query(x,rson);
}
void change(int u,int v,int add)
{
	int fu=top[u],fv=top[v];
	while(fu!=fv)				//往上一直找到在同一条重链中 
	{
		if(dep[fu]<dep[fv])
		{
			swap(u,v);swap(fu,fv);
		}
		update(id[fu],id[u],add,1,topw,1);//更新lazy标记
		u=fa[fu];
		fu=top[u];
	}
	if(u==v)								 
	{
		update(id[u],id[v],add,1,topw,1);
	}
	else
	{
		if(id[u]>id[v]) swap(u,v);			//编号小的在前面 
		update(id[u],id[v],add,1,topw,1);
		return ;
	}
}
int main()
{
	while(~scanf("%d%d%d",&n,&m,&p))
	{
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&val_pre[i]);
		}
		for(int i=1,a,b;i<=m;i++)
		{
            scanf("%d%d",&a,&b);
            G[a].push_back(b);
            G[b].push_back(a);
		}
		topw=0;
		dfs_1(1,0,1);
		dfs_2(1,1);
		for(int i=1;i<=n;i++)
		{
			val[id[i]]=val_pre[i];
		}
		build(1,topw,1); 
		char op[10];
        int a,b,c;
        while(p--)
        {
        	scanf("%s",op);
        	if(op[0]=='I')
        	{
                scanf("%d%d%d",&a,&b,&c);
                change(a,b,c);
			}
			else if(op[0]=='D')
			{
                scanf("%d%d%d",&a,&b,&c);
                change(a,b,-c);
			}
			else
			{
                scanf("%d",&a);
                int ans = query(id[a],1,topw,1);
                printf("%d\n",ans);
			}
		}
        for(int i=0;i<maxn;i++) G[i].clear();
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值