BZOJ 1036 树的统计 Count 树链剖分

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

n<=3W

裸的树链剖分。。。

这题没有区间修改所以我们可以大胆地套用ZKW线段树了(太久没写都忘光了的说)

注意修改点的时候要修改的是点的重标号而不是点本身 因为这事郁闷了一会

|点权|<=3W 3W*3W=9E 所以没必要开long long int完全能解决

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 30100
using namespace std;
struct abcd{
	int to,next;
}table[M<<1];
int head[M],tot;
void add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
int n,m,q,Max[70000],Sum[70000],fa[M],son[M],dpt[M],top[M],pos[M],siz[M],f[M],cnt;
char s[100];
void dfs1(int x)
{
	int i;
	dpt[x]=dpt[fa[x]]+1;
	siz[x]=1;
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==fa[x])
			continue;
		fa[table[i].to]=x;
		dfs1(table[i].to);
		if(siz[table[i].to]>siz[son[x]])
			son[x]=table[i].to;
		siz[x]+=siz[table[i].to];
	}
}
void dfs2(int x)
{
	int i;
	if(son[fa[x]]==x)
		top[x]=top[fa[x]];
	else
	{
		top[x]=x;
		for(i=x;i;i=son[i])
			pos[i]=++cnt;
	}
	Max[pos[x]+q]=Sum[pos[x]+q]=f[x];
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==fa[x])
			continue;
		dfs2(table[i].to);
	}
}
void Build_tree()
{
    for(int i=q-1;i;i--)
		Max[i]=max(Max[i<<1],Max[i<<1|1]),
		Sum[i]=Sum[i<<1]+Sum[i<<1|1];
}
void change(int x,int y)
{
    for(x+=q,Max[x]=Sum[x]=y,x>>=1;x;x>>=1)
		Max[x]=max(Max[x<<1],Max[x<<1|1]),
		Sum[x]=Sum[x<<1]+Sum[x<<1|1];
}
int getmax(int x,int y)
{
    int re=0x80000000;
    for(x+=q-1,y+=q+1;x^y^1;x>>=1,y>>=1)
    {
        if(~x&1)re=max(re,Max[x^1]);
        if( y&1)re=max(re,Max[y^1]);
    }
    return re;
}
int Qmax(int x,int y)
{
	int re=0x80000000,fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(dpt[fx]<dpt[fy])
			swap(x,y),swap(fx,fy);
		re=max( re , getmax(pos[fx],pos[x]) );
		x=fa[fx];fx=top[x];
	}
	if(dpt[x]<dpt[y])
		swap(x,y);
	re=max( re , getmax(pos[y],pos[x]) );
	return re;
}
int getsum(int x,int y)
{
    int re=0;
    for(x+=q-1,y+=q+1;x^y^1;x>>=1,y>>=1)
    {
        if(~x&1)re+=Sum[x^1];
        if( y&1)re+=Sum[y^1];
    }
    return re;
}
int Qsum(int x,int y)
{
	int re=0,fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(dpt[fx]<dpt[fy])
			swap(x,y),swap(fx,fy);
		re+=getsum(pos[fx],pos[x]);
		x=fa[fx];fx=top[x];
	}
	if(dpt[x]<dpt[y])
		swap(x,y);
	re+=getsum(pos[y],pos[x]);
	return re;
}
int main()
{
	int i,x,y;
	cin>>n;
	for(q=1;q<=n+1;q<<=1);
	for(i=1;i<n;i++)
		scanf("%d%d",&x,&y),add(x,y),add(y,x);
	for(i=1;i<=n;i++)
		scanf("%d",&f[i]);
	memset(Max,0xef,sizeof Max);
	dfs1(1);
	dfs2(1);
	Build_tree();
	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%s%d%d",s,&x,&y);
		if(s[1]=='M')
			printf("%d\n", Qmax(x,y) );
		else if(s[1]=='S')
			printf("%d\n", Qsum(x,y) );
		else
			change(pos[x],y);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值