树链剖分学习 (hdu3966)

题意:一棵树上每个节点有权值,定义三种操作:

Q  a  询问a号节点的权值

I  a  b  c    a到b路径上所有节点权值加上c(包括a,b)

D  a  b  c    a到b路径上所有节点权值减去c(包括a,b)

对每个Q,输出结果


树链剖分:将树上的节点(或者边)通过特殊的dfs表上序号,序号作为下标套上线段树。

特殊的dfs :  将边分为重边跟轻边,dfs时优先走重边.

重边:一个节点的连接的子树节点最多的边为重边

修改操作:若当前询问点为a , b    ,  fa,fb为a,b所在重链的最顶端且depth[fb]>depth[fa],则一次修改fb到b区间的数据,在使b=father[b] 直到 fa=fb

正确性:因为dfs 重链优先,则fb到b的上的节点的下标是连续的,修改则就跟线段树的修改一样 ,最坏情况执行log(n)次(a或b最多提升log(n)次到根节点)

接下来就是一般的线段树的操作了。


#include <iostream>
using namespace std;
#define M 100005
#include <string.h>
#include <string>


struct edges
{
	int x,y,v,next;
}edge[M*2];


int head[M],num,z;
int tree[M*4],w[M],lazy[M*4];
int ted[M][3];


inline max(int a,int b)
{
	return a>b?a:b;
}


void add_edge(int x,int y,int v)
{
	edge[num].x=x;
	edge[num].y=y;
	edge[num].v=v;
	edge[num].next=head[x];
	head[x]=num++;
}


int top[M],fat[M],son[M],siz[M],dep[M];
int n,m,q;


int dfs(int f,int t)
{
	fat[t]=f;
	dep[t]=dep[f]+1;
	siz[t]=1;
	son[t]=0;
	int i=head[t];
	int mx=0,sum=0;
	while(i)
	{
		if(edge[i].y!=f)
		{
			siz[t]+=dfs(t,edge[i].y);
			if( siz[son[t]] < siz[edge[i].y] )
				son[t]=edge[i].y;
		}
		i=edge[i].next;
	}
	return sum;
}


void  dfs2(int r,int st)
{
	top[r]=st;
	w[r]=++z;
	if( son[r] ) dfs2(son[r],st);
	int i=head[r];
	while(i)
	{
		int j=edge[i].y;
		if( j!=son[r] && j!=fat[r] )
			dfs2( j , j );
		i=edge[i].next;
	}
}



int nux[M];


void pushup(int root)
{
	tree[root]=max(tree[root<<1],tree[root<<1+1]);
}


void pushdown(int root,int v)
{
	if(lazy[root])
	{
		int l=root<<1,r=l+1,s=lazy[root];
		lazy[root]=0;
		lazy[l]+=s;
		lazy[r]+=s;
		tree[l]+=(v-v/2)*s;
		tree[r]+=v/2*s;
	}
}


void update2(int root,int low,int row,int l,int r,int v)
{
	if(l<=low && r>=row )
	{
		lazy[root]+=v;
		tree[root]+=v*(r-l+1);
		return ;
	}
	if(row<l  || low>r)
		return ;
	int ql=root*2,qr=ql+1,mid=(low+row)/2;
	pushdown(root,row-low+1);
	update2(ql,low,mid,l,r,v);
	update2(qr,mid+1,row,l,r,v);


	pushup(root);
}


void change(int l,int r,int v)
{
	while(top[l]!=top[r])
	{
		if( dep[top[l]] > dep[top[r]] ) swap(l,r);
		update2(1,1,z,w[top[r]],w[r],v);
		r=top[r];
	}
	if(dep[l]>dep[r])  swap(l,r);
	update2(1,1,z,w[l],w[r],v);
}




int mfind2(int root,int low,int hig,int loc)
{
	if( low==hig )
		return tree[root];
	pushdown(root,hig-low+1);
	int mid=(low+hig)/2,ret;
	if(loc<=mid) ret=mfind2(root*2,low,mid,loc);
	else ret=mfind2(root*2+1,mid+1,hig,loc);
	pushup(root);
	return ret;
}


/*---------------------------------------------------*/




void init2()
{
	int i,k,x,y;
	
	siz[0]=fat[0]=z=0,num=1;
	memset(head,0,sizeof(head));
	memset(top,0,sizeof(top));
	memset(son,0,sizeof(son));
	memset(tree,0,sizeof(tree));
	memset(lazy,0,sizeof(lazy));
	
	for(i=1;i<=n;i++)
		cin>>nux[i];


	for(i=1;i<=m;i++)
	{
		cin>>x>>y;
		add_edge(x,y,0);
		add_edge(y,x,0);
	}


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


	for(i=1;i<=n;i++)
		update2(1,1,z,w[i],w[i],nux[i]);


//	for(i=1;i<=n;i++)
//		cout<<son[i]<<' '<<top[i]<<' '<<fat[i]<<' '<<w[i]<<endl;
}


void work2()
{
	string ques;
	int a,b,c;
	while(q--)
	{
		cin>>ques;
		if(ques[0]=='Q')
		{
			cin>>a;
			cout<<mfind2(1,1,z,w[a])<<endl;
		}
		else 
		{
			cin>>a>>b>>c;
			if(ques[0]=='D')
				c=-c;
			change(a,b,c);
		}
	}
}


int main()
{


	while(cin>>n>>m>>q)
	{
		init2();
		work2();
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值