[BFS序+线段树]HDU 5957Query on a graph 题解

本文介绍了如何利用BFS序和线段树解决HDU 5957题目的方法。题目涉及在给定的基环树上进行两种操作:更新距离指定节点x不超过k的节点值,以及查询这些节点的数量。通过建立基环树并使用BFS序,可以将问题转化为区间修改和查询,线段树在此问题中起到关键作用。

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

题目大意

给出一个n个点n条边的图,有两种操作:

1.Uptate x k d 将所有距离x距离不超过k的所有节点加d

2.Query x k 统计所有距离x距离不超过k的所有节点

n ≤ 1 0 5 , 0 ≤ k ≤ 2 n\le 10^5,0\le k \le 2 n105,0k2

解题分析

基环树常用套路:将整个环当做一个根节点,然后建树。

然后用BFS建树的时候可以得到一个进队列的顺序,称之为BFS序(相对的也有DFS序)。

那么自己画个图就会发现一个节点的儿子节点和孙子节点在BFS序上都在一个区间内,那么直接区间修改,用线段树维护,但这道题不是树而是基环树,所以需要分类讨论一波,具体见代码。

示例代码

题目传送门

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100005,maxe=200005;
int n,q,tot,tst,son[maxe],nxt[maxe],lnk[maxn],a[maxn],fa[maxn],BCC[maxn];
int hed,til,ti,que[maxn],id[maxn],Ls[maxn],Rs[maxn],Lg[maxn],Rg[maxn];
bool vs[maxn]; char st[10];
struct Seg{
	int tL[maxn<<2],tR[maxn<<2],tg[maxn<<2];
	LL sum[maxn<<2];
	inline int getL(int x){return x>1?x-1:a[0];}
	inline int getR(int x){return x<a[0]?x+1:1;}
	void Build(int L,int R,int p){
		tL[p]=L; tR[p]=R; sum[p]=tg[p]=0; if (L==R) return;
		int mid=L+(R-L>>1); Build(L,mid,p<<1); Build(mid+1,R,p<<1|1);
	}
	inline void Pushup(int x){sum[x]=sum[x<<1]+sum[x<<1|1];}
	inline void Addtag(int x,int tem){sum[x]+=(LL)(tR[x]-tL[x]+1)*tem; tg[x]+=tem;}
	inline void Pushdown(int x){if (tg[x]){Addtag(x<<1,tg[x]); Addtag(x<<1|1,tg[x]); tg[x]=0;}}
	inline void Insert(int L,int R,int x,int tem){
		if (tL[x]>R||tR[x]<L) return; if (L<=tL[x]&&tR[x]<=R) {Addtag(x,tem); return;}
		int mid=tL[x]+(tR[x]-tL[x]>>1); Pushdown(x);
		if (R<=mid) Insert(L,R,x<<1,tem); else if (L>mid) Insert(L,R,x<<1|1,tem);
		else {Insert(L,mid,x<<1,tem); Insert(mid+1,R,x<<1|1,tem);} Pushup(x);
	}
	LL Ask(int L,int R,int x){
		if (tL[x]>R||tR[x]<L) return 0; if (L<=tL[x]&&tR[x]<=R) return sum[x];
		int mid=tL[x]+(tR[x]-tL[x]>>1); Pushdown(x);
		if (R<=mid) return Ask(L,R,x<<1); else if (L>mid) return Ask(L,R,x<<1|1);
		else return Ask(L,mid,x<<1)+Ask(mid+1,R,x<<1|1);
	}
	inline LL Askone(int x){return Ask(x,x,1);}
	inline void Addone(int x,int tem){Insert(x,x,1,tem);}
	void Uptate(int x,int k,int tem){
		Addone(id[x],tem);
		if (k>=1){
			Insert(Ls[x],Rs[x],1,tem);
			if (!BCC[x]) Addone(id[fa[x]],tem);
			else {Addone(id[a[getL(BCC[x])]],tem); Addone(id[a[getR(BCC[x])]],tem);}
		}
		if (k>=2){
			Insert(Lg[x],Rg[x],1,tem);
			if (!BCC[x]){
				Addone(id[x],-tem); Insert(Ls[fa[x]],Rs[fa[x]],1,tem);
				int p=BCC[fa[x]]; if (!p) Addone(id[fa[fa[x]]],tem);
				else {Addone(id[a[getL(p)]],tem); Addone(id[a[getR(p)]],tem);}
			}else{
				int BL=getL(BCC[x]),BR=getR(BCC[x]); Insert(Ls[a[BL]],Rs[a[BL]],1,tem); Insert(Ls[a[BR]],Rs[a[BR]],1,tem);
				if (getL(BL)!=BR) Addone(id[a[getL(BL)]],tem);
				if (getR(BR)!=BL&&getR(BR)!=getL(BL)) Addone(id[a[getR(BR)]],tem);
			}
		}
	}
	LL Query(int x,int k){
		LL ans=Askone(id[x]); //自己节点
		if (k>=1){
			ans+=Ask(Ls[x],Rs[x],1); //儿子区间
			if (!BCC[x]) ans+=Askone(id[fa[x]]); //如果不在环上,加父节点
			else ans+=Askone(id[a[getL(BCC[x])]])+Askone(id[a[getR(BCC[x])]]);
            //如果在环上,加环上的左右节点
		}
		if (k>=2){
			ans+=Ask(Lg[x],Rg[x],1);//孙子区间
			if (!BCC[x]){
				ans+=Ask(Ls[fa[x]],Rs[fa[x]],1)-Askone(id[x]);//兄弟节点
				int p=BCC[fa[x]]; if (!p) ans+=Askone(id[fa[fa[x]]]);
				else ans+=Askone(id[a[getL(p)]])+Askone(id[a[getR(p)]]);
			}else{
				int BL=getL(BCC[x]),BR=getR(BCC[x]); ans+=Ask(Ls[a[BL]],Rs[a[BL]],1)+Ask(Ls[a[BR]],Rs[a[BR]],1);
				if (getL(BL)!=BR) ans+=Askone(id[a[getL(BL)]]);
				if (getR(BR)!=BL&&getR(BR)!=getL(BL)) ans+=Askone(id[a[getR(BR)]]);
			}//左边的左边和右边的右边,要防取重
		}
		return ans;
	}
}T;
inline void readi(int &x){
	x=0; char ch=getchar(),lst='+';
	while ('0'>ch||ch>'9') {lst=ch; ch=getchar();}
	while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
	if (lst=='-') x=-x;
}
void _add(int x,int y){son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;}
void getBCC(int x,int y){for (int i=x;i!=y;i=fa[i]){a[++a[0]]=i; BCC[i]=a[0];} a[++a[0]]=y; BCC[y]=a[0];} //求环
void _dfs(int x,int dad){
	vs[x]=true;
	for (int j=lnk[x];j;j=nxt[j])
		if (son[j]!=dad) if (!vs[son[j]]){fa[son[j]]=x; _dfs(son[j],x);} else if (!BCC[x]) getBCC(x,son[j]);
}
void _bfs(int S){ //求BFS序
	hed=0; til=1; que[1]=S; id[S]=++ti;
	while (hed!=til){
		int x=que[++hed]; Ls[x]=ti+1;
		for (int j=lnk[x];j;j=nxt[j])
			if (!id[son[j]]&&!BCC[son[j]]){fa[son[j]]=x; que[++til]=son[j]; id[son[j]]=++ti;}
		Rs[x]=ti; //求x的儿子区间
	}
	for (int i=1;i<=til;i++){
		int x=que[i]; Lg[x]=2e9; Rg[x]=-2e9;
		for (int j=lnk[x];j;j=nxt[j])
			if (id[son[j]]>id[x]&&!BCC[son[j]]){Lg[x]=min(Lg[x],Ls[son[j]]); Rg[x]=max(Rg[x],Rs[son[j]]);} //求x的孙子区间
	}
}
int main()
{
	freopen("qtree0.in","r",stdin);
	freopen("qtree0.out","w",stdout);
	readi(tst);
	while (tst--){
		readi(n); tot=ti=a[0]=0;
		memset(lnk,0,sizeof(lnk)); memset(vs,0,sizeof(vs));
		memset(id,0,sizeof(id)); memset(BCC,0,sizeof(BCC));
		for (int i=1,x,y;i<=n;i++){readi(x); readi(y); _add(x,y); _add(y,x);}
		_dfs(1,0); for (int i=1;i<=a[0];i++) _bfs(a[i]); T.Build(1,ti,1);
		readi(q);
		for (int x,y,z;q;q--){
			scanf("%s",st); readi(x); readi(y);
			if (st[0]=='Q') printf("%lld\n",T.Query(x,y));
			else {readi(z); T.Uptate(x,y,z);}
		}
	}
	return 0;
}

话说回来如果只有区间修改的的话可以考虑树状数组,所以……这里就是个F(k)l(e)a(n)g(g)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值