SP375 QTREE - Query on a tree 题解

博客围绕一道树边权操作的题目展开。题目给定 n 个节点的树及边权,有修改边权和查询路径边权最小值两种操作。采用树链剖分的解法,将边权转化为点权,查询时避开最近公共祖先。还提到代码与树链剖分模板题相似,且给出了 c 语言代码。

双倍经验:P4114

update on 2023/2/15

这道题的 rmj 只允许交 c 语言的代码,我把代码改了改,在洛谷上过了。把 c 语言代码也贴上来。

题目大意

给定一颗 n n n 个节点的树,有 n − 1 n - 1 n1 条边,给定每条边的两个端点和边权。

这里有两种操作,询问以 DONE 结束。

  • 第一种为 CHANGE 操作,输入格式为 CHANGE i t,表示将第 i i i 条边的边权改为 t t t

  • 第二种为 QUERY 操作,输入格式为 QUERY a b,表示查询从节点 a a a 到节点 b b b 的路径上边权最小值。

注意:本题多组数据,请在每轮循环结束后清空所有边。

解法

考虑树链剖分做法。

看到询问边权,我们考虑将边权转化为点权,即把一条链接这父节点和子节点的边的边权转化为子节点的点权。

对于两个节点,它们之间的路径一定会经过两个点的最近公共祖先,如下图。节点 1 1 1 和节点 3 3 3 之间的路径经过了节点 2 2 2,而我们将 4 4 4 2 2 2 的边权转化为了点 2 2 2 的点权,我们要避开他们的最近公共祖先,这个只要在查询时查询从 i d a + 1 id_a +1 ida+1 i d b id_b idb 之间的点权最大值即可。代码与树链剖分模板题很相似,大部分可以直接粘过来用。

代码

//这是 c++ 代码
#include<bits/stdc++.h>
using namespace std;
#define ls l,mid,rt*2
#define rs mid+1,r,rt*2+1
#define lc rt*2
#define rc rt*2+1
#define Getchar() p1==p2 and (p2=(p1=Inf)+fread(Inf,1,1<<21,stdin),p1==p2)?EOF:*p1++
#define Putchar(c) p3==p4 and (fwrite(Ouf,1,1<<21,stdout),p3=Ouf),*p3++=c
char Inf[1<<21],Ouf[1<<21],*p1,*p2,*p3=Ouf,*p4=Ouf+(1<<21);
inline void read(int &x,char c=Getchar())
{
	bool f=c!='-';
	x=0;
	while(c<48 or c>57) c=Getchar(),f&=c!='-';
	while(c>=48 and c<=57) x=(x<<3)+(x<<1)+(c^48),c=Getchar();
	x=f?x:-x;
}
inline void write(int x)
{
	if(x<0) Putchar('-'),x=-x;
	if(x>=10) write(x/10),x%=10;
	Putchar(x^48);
}
int t,n,dep[10010],f[10010],siz[10010],hvson[10010],id[10010],belong[10010],start[10010],neww[10010],cnt;
int sum[40010];
pair<int,int> Pair[10010];
char ch;
struct edge
{
	int to,cost;
};
vector<edge> v[100010];
inline void dfs1(int pos,int fa,int depth,int maxi=-0x3f3f3f3f)
{
	dep[pos]=depth,f[pos]=fa,siz[pos]=1;
	for(int i=0;i<v[pos].size();i++)
	{
		if(v[pos][i].to!=fa)
		{
			neww[v[pos][i].to]=v[pos][i].cost,dfs1(v[pos][i].to,pos,depth+1),siz[pos]+=siz[v[pos][i].to];
			if(siz[v[pos][i].to]>maxi) hvson[pos]=v[pos][i].to,maxi=siz[v[pos][i].to];
		}
	}
}
inline void dfs2(int pos,int Start)
{
	id[pos]=++cnt,belong[cnt]=pos,start[pos]=Start;
	if(hvson[pos])
	{
		dfs2(hvson[pos],Start);
		for(int i=0;i<v[pos].size();i++)
			if(v[pos][i].to!=f[pos] && v[pos][i].to!=hvson[pos]) dfs2(v[pos][i].to,v[pos][i].to);
	}
}
inline void pushup(const int &rt)
{
	sum[rt]=max(sum[lc],sum[rc]);
}
inline void build(int l,int r,int rt)
{
	if(l==r)
	{
		sum[rt]=neww[belong[l]];
		return;
	}
	int mid=(l+r)/2;
	build(ls),build(rs),pushup(rt);
}
inline void fix(const int &pos,const int &val,int l,int r,int rt)
{
	if(l==r)
	{
		sum[rt]=val;
		return;
	}
	int mid=(l+r)/2;
	if(pos<=mid) fix(pos,val,ls);
	else fix(pos,val,rs);
	pushup(rt);
}
inline int ask(const int &L,const int &R,int l,int r,int rt)
{
	if(L>R) return 0;
	if(L<=l && r<=R) return sum[rt];
	int mid=(l+r)/2,ret=0;
	if(L<=mid) ret=max(ret,ask(L,R,ls));
	if(mid<R) ret=max(ret,ask(L,R,rs));
	return ret;
}
inline int ask(int x,int y)
{
	if(x==y) return 0;
	int ret=0;
	while(start[x]!=start[y])
	{
		if(dep[start[x]]<dep[start[y]]) swap(x,y);
		ret=max(ret,ask(id[start[x]],id[x],1,n,1)),x=f[start[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ret=max(ret,ask(id[x]+1,id[y],1,n,1));
	return ret;
}
int main()
{
	read(t);
	while(t--)
	{
		read(n),cnt=0,memset(hvson,0,sizeof(hvson));
		for(int i=1,x,y,z;i<n;i++)
			read(x),read(y),read(z),v[x].push_back((edge){y,z}),v[y].push_back((edge){x,z}),Pair[i]=make_pair(x,y);
		dfs1(1,0,1),dfs2(1,1),build(1,n,1);
		for(int x,y;;)
		{
			ch=Getchar();
			while(ch<'A' || ch>'Z') ch=Getchar();
			if(ch=='D') break;
			read(x),read(y);
			if(ch=='C')
			{
				if(f[Pair[x].second]==Pair[x].first) swap(Pair[x].first,Pair[x].second);
				fix(id[Pair[x].first],y,1,n,1);
			}
			else write(ask(x,y)),Putchar('\n');
		}
		for(int i=1;i<=n;i++) v[i].clear();
	}
	fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
	return 0;
}
//这是 c 语言代码
#include<stdio.h>
#include<stdlib.h>
#define ls l,mid,rt*2
#define rs mid+1,r,rt*2+1
#define lc rt*2
#define rc rt*2+1
#define getchar() p1==p2 && (p2=(p1=inf)+fread(inf,1,1<<21,stdin),p1==p2)?EOF:*p1++
#define putchar(c) p3==p4 && (fwrite(ouf,1,1<<21,stdout),p3=ouf),*p3++=c
char inf[1<<21],ouf[1<<21],*p1,*p2,*p3=ouf,*p4=ouf+(1<<21);
int read()
{
	char c=getchar();
	int f=(c!='-'),x=0;
	while(c<48 || c>57) c=getchar(),f&=c!='-';
	while(c>=48 && c<=57) x=(x<<3)+(x<<1)+(c^48),c=getchar();
	x=f?x:-x;
	return x;
}
void write(int x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>=10) write(x/10),x%=10;
	putchar(x^48);
}
int t,n,dep[10010],f[10010],siz[10010],hvson[10010],id[10010],belong[10010],start[10010],neww[10010],cnt,sum[40010];
int head[100010],tot;
struct pair
{
	int first,second;
};
struct pair make_pair(const int x,const int y)
{
	struct pair ret;
	ret.first=x,ret.second=y;
	return ret;
}
struct pair Pair[10010];
char ch;
struct Edge
{
	int to,cost,next;
};
struct Edge edge[200010];
void add(const int x,const int y,const int z)
{
	edge[++tot].to=y,edge[tot].cost=z,edge[tot].next=head[x],head[x]=tot;
}
int max(const int x,const int y)
{
	return (x>y?x:y);
}
void swap(int *x,int *y)
{
	int t;
	t=*x,*x=*y,*y=t;
}
void dfs1(int pos,int fa,int depth)
{
	int maxi=-0x3f3f3f3f;
	dep[pos]=depth,f[pos]=fa,siz[pos]=1;
	for(int i=head[pos];i;i=edge[i].next)
	{
		if(edge[i].to!=fa)
		{
			neww[edge[i].to]=edge[i].cost,dfs1(edge[i].to,pos,depth+1),siz[pos]+=siz[edge[i].to];
			if(siz[edge[i].to]>maxi) hvson[pos]=edge[i].to,maxi=siz[edge[i].to];
		}
	}
}
void dfs2(int pos,int Start)
{
	id[pos]=++cnt,belong[cnt]=pos,start[pos]=Start;
	if(hvson[pos])
	{
		dfs2(hvson[pos],Start);
		for(int i=head[pos];i;i=edge[i].next)
			if(edge[i].to!=f[pos] && edge[i].to!=hvson[pos]) dfs2(edge[i].to,edge[i].to);
	}
}
void pushup(const int rt)
{
	sum[rt]=max(sum[lc],sum[rc]);
}
void build(int l,int r,int rt)
{
	if(l==r)
	{
		sum[rt]=neww[belong[l]];
		return;
	}
	int mid=(l+r)/2;
	build(ls),build(rs),pushup(rt);
}
void fix(const int pos,const int val,int l,int r,int rt)
{
	if(l==r)
	{
		sum[rt]=val;
		return;
	}
	int mid=(l+r)/2;
	if(pos<=mid) fix(pos,val,ls);
	else fix(pos,val,rs);
	pushup(rt);
}
int query(const int L,const int R,int l,int r,int rt)
{
	if(L<=l && r<=R) return sum[rt];
	int mid=(l+r)/2,ret=0;
	if(L<=mid) ret=max(ret,query(L,R,ls));
	if(mid<R) ret=max(ret,query(L,R,rs));
	return ret;
}
int ask(int x,int y)
{
	int ret=0;
	while(start[x]!=start[y])
	{
		if(dep[start[x]]<dep[start[y]]) swap(&x,&y);
		ret=max(ret,query(id[start[x]],id[x],1,n,1)),x=f[start[x]];
	}
	if(dep[x]>dep[y]) swap(&x,&y);
	ret=max(ret,query(id[x]+1,id[y],1,n,1));
	return ret;
}
int main()
{
	t=read();
	while(t--)
	{
		n=read(),cnt=0;
		for(int i=1,x,y,z;i<n;i++)
			x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z),Pair[i]=make_pair(x,y);
		dfs1(1,0,1),dfs2(1,1),build(1,n,1);
		for(int x,y;;)
		{
			ch=getchar();
			while(ch<'A' || ch>'Z') ch=getchar();
			if(ch=='D') break;
			x=read(),y=read();
			if(ch=='C')
			{
				if(f[Pair[x].second]==Pair[x].first) swap(&Pair[x].first,&Pair[x].second);
				fix(id[Pair[x].first],y,1,n,1);
			}
			else write(ask(x,y)),putchar('\n');
		}
		for(int i=1;i<=n;i++) head[i]=0,hvson[i]=0;
	}
	fwrite(ouf,1,p3-ouf,stdout),fflush(stdout);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值