HDU5957 Query on a graph

线段树 BFS序

题目传送门

题目大意: 一个基环树,有点权。有询问和修改操作。修改有三个参数 u , k , d u,k,d u,k,d,表示把距离 u u u不超过 k k k(包括自己,下同)的点都加上 d d d。查询有两个参数 u , k u,k u,k,表示求距离 u u u不超过 k k k的点权和。 n ≤ 1 e 5 , k ≤ 2 n\leq 1e5,k\leq 2 n1e5,k2

先别管那条非树边。当 k = 1 k=1 k=1的时候是维护它爸爸和它儿子,当 k = 2 k=2 k=2的时候还要维护它爷爷、它兄弟和它孙子。

我们对这棵树求一遍BFS序,这样当 k = 1 k=1 k=1时要维护的就是两个点和一个区间,当 k = 2 k=2 k=2时就是两个点和三个区间。线段树维护即可。对非树边的影响特殊处理。

具体实现比较繁琐,详见代码:

#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
typedef long long LL;
struct edge{ int nxt,to; }ed[N<<1];
struct tree{ int l,r; LL x,f; }t[N<<2];
int n,m,k,ti,v1,v2,h[N],fa[N],in[N],l[N][3],r[N][3];
queue <int> q; bool f[N],f1[N],f2[N];
//l/r[x][0]存它儿子的区间,[x][1]存它孙子的区间,[x][2]存它兄弟的区间
//f1/f2表示到它距离为1的点中有没有非树边连的两个点
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F int _read(){
	int x=0,f=1; char ch=readc();
	while (!isdigit(ch)&&ch!='M'&&ch!='Q'){ if (ch=='-') f=-1; ch=readc(); }
	if (ch=='M'||ch=='Q') return ch;
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x*f;
}
F void bfs(){
	for (int i=0;i<=n;i++) l[i][0]=1e9;
	fa[1]=0,f[1]=true,q.push(1);
	while (!q.empty()){
		int x=q.front(); in[x]=++ti,q.pop();
		r[fa[x]][0]=max(r[fa[x]][0],ti);
		l[fa[x]][0]=min(l[fa[x]][0],ti);
		for (int i=h[x],v;i;i=ed[i].nxt)
			if ((v=ed[i].to)!=fa[x]&&!f[v])
				fa[v]=x,f[v]=true,q.push(v);
	}
}
#define add(x,y) ed[++k]=(edge){h[x],y},h[x]=k
int findfa(int x){ return x==fa[x]?x:fa[x]=findfa(fa[x]); }
F void pshp(int x){ t[x].x=t[x<<1].x+t[x<<1|1].x; }
F void add1(int x,LL w){ t[x].x+=w*(t[x].r-t[x].l+1),t[x].f+=w; }
F void pshd(int x){ if (t[x].f) add1(x<<1,t[x].f),add1(x<<1|1,t[x].f),t[x].f=0;  }
void build(int x,int l,int r){
	t[x]=(tree){l,r,0,0}; if (l==r) return; int mid=l+r>>1;
	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
}
void mdfy(int x,int l,int r,LL w){
	if (t[x].l>r||t[x].r<l) return;
	if (t[x].l>=l&&t[x].r<=r) return add1(x,w);
	pshd(x),mdfy(x<<1,l,r,w),mdfy(x<<1|1,l,r,w),pshp(x);
}
LL srch(int x,int l,int r){
	if (t[x].l>r||t[x].r<l) return 0;
	if (t[x].l>=l&&t[x].r<=r) return t[x].x;
	return pshd(x),srch(x<<1,l,r)+srch(x<<1|1,l,r);
}
F bool pd(int y,int x){
	if (fa[x]==y||fa[fa[x]]==y) return false;
	for (int i=0;i<3;i++)
		if (in[y]>=l[x][i]&&in[y]<=r[x][i]) return false;
	return true;
}
F void nsrt(){//分类讨论修改范围,下同
	int x=_read(),k=_read(),d=_read();
	bool ff1=true,ff2=true;
	if (k==0) mdfy(1,in[x],in[x],d);
	if (k==1){
		mdfy(1,in[x],in[x],d),mdfy(1,l[x][0],r[x][0],d);
		if (fa[x]) mdfy(1,in[fa[x]],in[fa[x]],d);
		if (x==v1) mdfy(1,in[v2],in[v2],d);
		if (x==v2) mdfy(1,in[v1],in[v1],d);
	}
	if (k==2){
		for (int i=0;i<3;i++) mdfy(1,l[x][i],r[x][i],d);
		if (x==v1){//对非树边连的两个点特判
			if (pd(v2,x)) mdfy(1,in[v2],in[v2],d);
			if (pd(fa[v2],x)) mdfy(1,in[fa[v2]],in[fa[v2]],d);
			mdfy(1,l[v2][0],r[v2][0],d),ff1=fa[fa[x]]!=v2,ff2=fa[fa[fa[x]]]!=v2;
			//要修改到另一个点的距离为1的点,注意不要重复
		}
		if (x==v2){
			if (pd(v1,x)) mdfy(1,in[v1],in[v1],d);
			if (pd(fa[v1],x)) mdfy(1,in[fa[v1]],in[fa[v1]],d);
			mdfy(1,l[v1][0],r[v1][0],d),ff1=fa[fa[x]]!=v1,ff2=fa[fa[fa[x]]]!=v1;
		}
		if (fa[x]&&ff1) mdfy(1,in[fa[x]],in[fa[x]],d);
		if (fa[fa[x]]&&ff2) mdfy(1,in[fa[fa[x]]],in[fa[fa[x]]],d);
		if (f1[x]&&pd(v2,x)) mdfy(1,in[v2],in[v2],d);
		if (f2[x]&&pd(v1,x)) mdfy(1,in[v1],in[v1],d);
	}
}
F void find(){
	int x=_read(),k=_read(); LL ans=0;
	bool ff1=true,ff2=true;
	if (k==0) ans+=srch(1,in[x],in[x]);
	if (k==1){
		ans+=srch(1,in[x],in[x])+srch(1,l[x][0],r[x][0]);
		if (fa[x]) ans+=srch(1,in[fa[x]],in[fa[x]]);
		if (x==v1) ans+=srch(1,in[v2],in[v2]);
		if (x==v2) ans+=srch(1,in[v1],in[v1]);
	}
	if (k==2){
		for (int i=0;i<3;i++) ans+=srch(1,l[x][i],r[x][i]);
		if (x==v1){
			if (pd(v2,x)) ans+=srch(1,in[v2],in[v2]);
			if (pd(fa[v2],x)) ans+=srch(1,in[fa[v2]],in[fa[v2]]);
			ans+=srch(1,l[v2][0],r[v2][0]),ff1=fa[fa[x]]!=v2,ff2=fa[fa[fa[x]]]!=v2;
		}
		if (x==v2){
			if (pd(v1,x)) ans+=srch(1,in[v1],in[v1]);
			if (pd(fa[v1],x)) ans+=srch(1,in[fa[v1]],in[fa[v1]]);
			ans+=srch(1,l[v1][0],r[v1][0]),ff1=fa[fa[x]]!=v1,ff2=fa[fa[fa[x]]]!=v1;
		}
		if (fa[x]&&ff1) ans+=srch(1,in[fa[x]],in[fa[x]]);
		if (fa[fa[x]]&&ff2) ans+=srch(1,in[fa[fa[x]]],in[fa[fa[x]]]);
		if (f1[x]&&pd(v2,x)) ans+=srch(1,in[v2],in[v2]);
		if (f2[x]&&pd(v1,x)) ans+=srch(1,in[v1],in[v1]);
	}
	printf("%lld\n",ans);
}
int main(){
	for (int T=_read();T;T--){
		n=_read(),k=ti=0;
		memset(h,0,sizeof(h)),memset(f,0,sizeof(f));
		memset(l,0,sizeof(l)),memset(r,0,sizeof(r));
		for (int i=1;i<=n;i++) fa[i]=i;
		for (int i=1;i<=n;i++){
			int x=_read(),y=_read(),fx,fy;
			if ((fx=findfa(x))==(fy=findfa(y))) v1=x,v2=y;
			else add(x,y),add(y,x),fa[fx]=fy;
		}
		bfs(),m=_read();
		for (int x=1;x<=n;x++){
			l[x][1]=1e9,f1[x]=fa[x]==v1,f2[x]=fa[x]==v2;
			for (int i=h[x],v;i;i=ed[i].nxt){
				if ((v=ed[i].to)==fa[x]) continue;
				f1[x]|=(v=ed[i].to)==v1,f2[x]|=v==v2;
				l[x][1]=min(l[x][1],l[v][0]);
				r[x][1]=max(r[x][1],r[v][0]);
			}
			l[x][2]=l[fa[x]][0],r[x][2]=r[fa[x]][0];
		}
		for (build(1,1,n);m;m--) _read()=='M'?nsrt():find();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值