bzoj 1036

本文介绍了一种结合树剖分与线段树的数据结构优化方法,用于解决特定类型的查询问题。通过树剖分预处理,配合线段树记录路径上的最大值和总和,实现了高效的区间查询。代码实现包括树剖分、线段树建立及更新查询等关键步骤。

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

水题

裸树剖

线段树记录最大值与和。

代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;

int n,num=0,en=0,tn=0,fst[30010],q;
struct edge
{
	int x,y,n;
}e[60010];
struct tree
{
	int l,r,lc,rc,c1,c2;
}tr[60010];
struct pnt
{
	int f,tp,s,dp,sz,en;
}p[30010];

void ins(int x,int y)
{
	e[++num]={x,y,fst[x]};
	fst[x]=num;
}
void sd1(int x,int f)
{
	p[x].f=f;
	p[x].sz=1;
	p[x].dp=p[f].dp+1;
	for(int i=fst[x];i;i=e[i].n)
	{
		int y=e[i].y;
		if(y==f)
		continue;
		sd1(y,x);
		if(p[y].sz>p[p[x].s].sz)
		p[x].s=y;
		p[x].sz+=p[y].sz;
	}
}
void sd2(int x,int tp)
{
	p[x].en=++en;
	p[x].tp=tp;
	if(p[x].s)
	{
		sd2(p[x].s,tp);
		for(int i=fst[x];i;i=e[i].n)
		{
			int y=e[i].y;
			if(y==p[x].s||y==p[x].f)
			continue;
			sd2(y,y);
		}
	}
}
int bt(int l,int r)
{
	int i=++tn;
	tr[i]={l,r,-1,-1,0,0};
	if(l<r)
	{
		int md=l+r>>1;
		tr[i].lc=bt(l,md);
		tr[i].rc=bt(md+1,r);
	}
	return i;
}
void chg(int i,int x,int p)
{
	if(tr[i].l==tr[i].r)
	{
		tr[i].c1=tr[i].c2=x;
		return;
	}
	int md=tr[i].l+tr[i].r>>1,lc=tr[i].lc,rc=tr[i].rc;
	if(p<=md)
	chg(lc,x,p);
	else
	chg(rc,x,p);
	tr[i].c1=tr[lc].c1+tr[rc].c1;
	tr[i].c2=max(tr[lc].c2,tr[rc].c2);
}
int get(int i,int l,int r,bool tf)
{
	if(tr[i].l==l&&tr[i].r==r)
	{
		if(tf)
		return tr[i].c1;
		else
		return tr[i].c2;
	}
	int md=tr[i].l+tr[i].r>>1,lc=tr[i].lc,rc=tr[i].rc;
	if(r<=md)
	return get(lc,l,r,tf);
	else if(l>md)
	return get(rc,l,r,tf);
	else
	{
		if(tf)
		return get(lc,l,md,tf)+get(rc,md+1,r,tf);
		else
		return max(get(lc,l,md,tf),get(rc,md+1,r,tf));
	}
}
int qr(int x,int y,bool tf)
{
	int tx=p[x].tp,ty=p[y].tp,ans;
	if(tf)
	ans=0;
	else
	ans=-2147483647;
	while(tx!=ty)
	{
		if(p[tx].dp<p[ty].dp)
		{
			swap(tx,ty);
			swap(x,y);
		}
//		printf("%d %d\n",tx,x);
		int s=get(1,p[tx].en,p[x].en,tf);
		if(tf)
		ans+=s;
		else
		ans=max(ans,s);
		x=p[tx].f;
		tx=p[x].tp;
	}
	if(p[x].dp>p[y].dp)
	swap(x,y);
	int s=get(1,p[x].en,p[y].en,tf);
	if(tf)
	ans+=s;
	else
	ans=max(ans,s);
	return ans;
}
int main()
{
	memset(fst,0,sizeof(fst));
	memset(p,0,sizeof(p));
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ins(x,y);
		ins(y,x);
	}
	sd1(1,0);
	sd2(1,1);
	bt(1,en);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		chg(1,x,p[i].en);
	}/*
	for(int i=1;i<=tn;i++)
	printf("%d %d %d %d %d %d\n",tr[i].l,tr[i].r,tr[i].lc,tr[i].rc,tr[i].c1,tr[i].c2);*/
	scanf("%d",&q);
	for(int i=0;i<q;i++)
	{
		char s[10];
		int x,y;
		scanf("%s%d%d",s,&x,&y);
		if(s[1]=='M')
		printf("%d\n",qr(x,y,0));
		else if(s[1]=='S')
		printf("%d\n",qr(x,y,1));
		else
		chg(1,y,p[x].en);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值