abc187 --E - Through Path(树上差分)/ 树链剖分+线段树

题目描述:

给你一棵树每个点的初始权值为0
N-1条边,每条边连接a[i] - b[i]
然后给你q个操作(两种)

  1. 以a[i]为起点遍历所有的点且路径上不能经过b[i],路径上的所有点加w

  2. 以b[i]为起点遍历所有的点且路径上不能经过a[i],路径上的所有点加w

题目思路(solution 1):

两种操作实质是一样的就是 起点不同
如果起点为u ,不能经过的点为v
如果dep[u] < dep[v] 就是把除了v的子树外的所有点加w
如果dep[u] >dep[v] 就是只把u的子树加w

具体操作:

先预处理出每个节点的深度
假设根节点为1
然后对于dep[u] < dep[v] ,我们把整棵树都加w是num[1]+=w,然后把num[v]减去w
对于dep[u] >dep[v] num[u]+=w

CODE:

ll  n,q,fa[maxn],num[maxn],a[maxn],b[maxn],ans[maxn],dep[maxn];
vector<ll>e[maxn];
void pre_work(int u,int p) {
	fa[u] = p ,	dep[u] = dep[p]+1;
	for(int v:e[u]) if(v!=p) pre_work(v,u);
}
void dfs(int  u,int  p,ll  s) {
	s+=num[u];
	ans[u] = s;
	for(int v:e[u]) {
		if(v==p) continue;
		dfs(v,u,s);
	}
}
int main() {
	n=read();
	for(int i=1 ; i<=n-1 ; i++) {
		int u = read();
		int v = read();
		a[i]=u,b[i]=v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	pre_work(1,0);
	q=read();
	while(q--) {
		int t = read();
		int id = read(),u,v;
		ll w = read();
		u=a[id],v=b[id];
		if(t==2) swap(u,v);
		if(dep[u]>dep[v]) num[u]+=w;
		else num[1]+=w,num[v]-=w;
	}
	dfs(1,0,0);
	rep(i,1,n) printf("%lld\n",ans[i]);
	return 0;
}

(solution 2)

也可以dfs序列后用线段树
也可树剖后用线段树,练练树剖吧,就是代码又臭又长
就是转化成序列后,区间修改,单点查询
CODE:

ll  n,q,fa[maxn],a[maxn],b[maxn],dep[maxn];
ll son[maxn],size_s[maxn],top[maxn],id[maxn],idex,od[maxn];
vector<ll>e[maxn];
struct node {
	ll l,r,lz,sum;
} num[maxn*4];
void pre_work(int u,int p) {
	fa[u] = p,dep[u] = dep[p]+1;
	size_s[u] = 1;
	for(int v:e[u]) {
		if(v==p) continue;
		pre_work(v,u);
		size_s[u]+=size_s[v];
		if(son[u]==0||size_s[son[u]]<size_s[v]) son[u] = v;
	}
}

void work(int u,int top_v) {
	id[u] = ++idex;
	od[idex] = u;
	top[u] = top_v;
	if(son[u]==0) return ;
	work(son[u],top_v);
	for(int v:e[u]) {
		if(v==fa[u]||v==son[u]) continue;
		work(v,v);
	}
}
void push_up(int st) {
	num[st].sum = num[st*2].sum + num[st*2+1].sum;
}
void push_down(int st) {
	if(num[st].lz==0) return ;
	num[st*2].lz+=num[st].lz;
	num[st*2+1].lz+=num[st].lz;

	num[st*2].sum += (num[st*2].r - num[st*2].l+1)*num[st].lz;
	num[st*2+1].sum += (num[st*2+1].r - num[st*2+1].l+1)*num[st].lz;
	num[st].lz =0 ;
}
void build(int st,int l,int r) {
	num[st].l = l,num[st].r = r;
	if(l==r) {
		num[st].lz=	num[st].sum =0 ;
		return ;
	}
	int mid = (l+r)>>1;
	build(st*2,l,mid);
	build(st*2+1,mid+1,r);
	push_up(st);
}
void update(int st,int l,int r,int ql,int qr,ll w) {
	if(ql<=l&&qr>=r) {
		num[st].lz+=w;
		num[st].sum += (num[st].r-num[st].l+1)*w*1ll;
		return ;
	}
	int mid = (l+r)>>1;
	if(ql<=mid) update(st*2,l,mid,ql,qr,w);
	if(qr>mid) update(st*2+1,mid+1,r,ql,qr,w);
	push_up(st);
}
ll query(int st,int l,int r,int pos) {
	if(l==r) {
		return num[st].sum;
	}
	push_down(st);
	int mid = (l+r)>>1;
	if(pos<=mid) return query(st*2,l,mid,pos);
	else return query(st*2+1,mid+1,r,pos);
	push_up(st);
}
int main() {
	n=read();
	for(int i=1 ; i<=n-1 ; i++) {
		int u = read();
		int v = read();
		a[i]=u,b[i]=v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	pre_work(1,0);
	work(1,1);
	build(1,1,n);
	q=read();
	while(q--) {
		int t = read();
		int ei = read(),u,v;
		ll w = read();
		u=a[ei],v=b[ei];
		if(t==2) swap(u,v);
		if(dep[u]>dep[v]) {
			update(1,1,n,id[u],id[u]+size_s[u]-1,w);
		} else {
			update(1,1,n,id[v],id[v]+size_s[v]-1,-w);
			update(1,1,n,id[1],id[1]+size_s[1]-1,w);
		}
	}
	rep(i,1,n) printf("%lld\n",query(1,1,n,id[i]));

	return 0;
}

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值