【NOIP】test

【题目背景】

Tom在学写动态树,但是做题时过了样例,提交RE。Tom抓住Jerry要他写个暴力来对拍。Jerry觉得这任务太简单了,就让你来完成一下。
在这里插入图片描述
【题目描述】
有一棵n个节点的树,初始时根节点为1。现在要支持如下操作——1、将某节点设置为根;2、改变某节点权值;3、询问以某节点为根的子树内节点权值之和;4、询问以某两点 为端点的链上的节点权值之和。
【输入格式】
第一行两个正整数n和q,表示树的节点数和操作个数。
接着n-1行每行两个整数u和v,表示有连接u和v的一条边。
随后一行n个正整数,表示每个点的初始权值。
之后q行每行格式如下:
首先一个范围为1~4的正整数,表示该操作类型。
对于1操作,之后一个正整数x,表示将x节点设置为根。
对于2操作,之后两个正整数x和v,表示将x节点的权值改为v。
对于3操作,之后一个正整数x,表示询问以x为根的子树内节点权值之和。
对于4操作,之后两个正整数x和y,表示询问以x和y为端点的链上的节点权值之和。
【输出格式】
对于每个操作3和操作4,输出一行一个整数表示询问的答案。
【输入样例1】
3 3
1 2
2 3
3 2 1
3 1
1 2
3 1
【输出样例1】
6
3
【输入样例2】
1 3
1
4 1 1
2 1 2
4 1 1
【输出样例2】
1
2
题解:
裸的树链剖分,最后重新记录换了根对答案的影响

#include<bits/stdc++.h>
using namespace std;
int n,q;
int head[200005];
int next[200005];
int ver[200005];
int tot,cnt;
int size[100005];
int zson[100005];
int d[100005];
int id[100005];
int fa[100005];
int top[100005];
int val[100005];
int nowval[100005];
int tree[400005];
int root,calc;
int read(){
	int num=0;
	char ch=getchar();
	while(ch>'9'||ch<'0'){
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		num=(num<<1)+(num<<3)+ch-'0';
		ch=getchar();
	}
	return num;
}
void add(int x,int y){
	ver[++tot]=y;
	next[tot]=head[x];
	head[x]=tot;
} 
void dfs1(int x,int fat,int deep){
	size[x]=1;
	fa[x]=fat;
	d[x]=deep;
	for(int i=head[x];i;i=next[i]){
		if(ver[i]==fat) continue;
		dfs1(ver[i],x,deep+1);
		size[x]+=size[ver[i]];
		if(size[ver[i]]>size[zson[x]]){
			zson[x]=ver[i];
		}
	}
}
void dfs2(int x,int topp){
	id[x]=++cnt;
	nowval[cnt]=val[x];
	top[x]=topp;
	if(!zson[x]) return;
	dfs2(zson[x],topp);
	for(int i=head[x];i;i=next[i]){
		if(ver[i]==zson[x] || ver[i]==fa[x])continue;
		dfs2(ver[i],ver[i]);
	}
}
void build(int node,int l,int r){
	if(l==r){
		tree[node]=nowval[l];
		return;
	}
	int mid=(l+r)>>1;
	build(node<<1,l,mid);
	build((node<<1)|1,mid+1,r);
	tree[node]=tree[node<<1]+tree[(node<<1)|1];
}
void update(int node,int l,int r,int goal,int v){
	 if(l==r){
	 	calc=tree[node];
	 	tree[node]=v;
	 	return;
	 }
	 int mid=(l+r)>>1;
	 if(goal<=mid){
	 	update(node<<1,l,mid,goal,v);
	 }
	 else update((node<<1)|1,mid+1,r,goal,v);
	 tree[node]=tree[node]-calc+v;
}
int check(int x,int y){
	if(x==y)return -1;
	if(d[x]>d[y])return 0;
	while(d[x]<=d[y]){
		if(fa[y]==x)
			return y;
		y=fa[y];
	}
	return 0;
}
void work(int now,int l,int r,int L,int R){
	if(l>=L && r<=R){
		calc+=tree[now];
		return;
	}
	else{
		int mid=(l+r)>>1;
		if(L<=mid)
			work(now<<1,l,mid,L,R);
		if(R>mid)
			work((now<<1)|1,mid+1,r,L,R);
	}
}
int hhh(int x,int y){
	int ans=0;
	while(top[x]!=top[y]){
		if(d[top[x]]<d[top[y]]) swap(x,y);
		calc=0;
		work(1,1,n,id[top[x]],id[x]);
		ans+=calc;
		x=fa[top[x]];
	}
	if(d[x]>d[y]) swap(x,y);
	calc=0;
	work(1,1,n,id[x],id[y]);
	ans+=calc;
	return ans;
}
int main(){
	n=read(),q=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=n;i++)
		val[i]=read();
	size[0]=-1;
	dfs1(1,0,1);
	dfs2(1,1);
	build(1,1,n);
	int k,x,v,y;
	for(int i=1;i<=q;i++){
		k=read();
		if(k==1){
			root=read();
			continue;
		}
		if(k==2){
			x=read(),v=read();
			update(1,1,n,id[x],v);
			continue;
		}
		if(k==3){
			x=read();
			int q=check(x,root);
			if(q==0){
				calc=0;
				work(1,1,n,id[x],id[x]+size[x]-1);
				printf("%d\n",calc);
				continue;
			}
			if(q==-1){
				calc=0;
				work(1,1,n,id[1],id[1]+size[1]-1);
				printf("%d\n",calc);
				continue;
			}
			calc=0;
			work(1,1,n,id[1],id[1]+size[1]-1);
			int num1=calc;
			calc=0;
			work(1,1,n,id[q],id[q]+size[q]-1);
			printf("%d\n",num1-calc);
			continue;
		}
		if(k==4){
			x=read();
			y=read();
			printf("%d\n",hhh(x,y));
		}
		
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值