【算法每日一练】图论(树论)篇17 最大子树和,树上异或,树的分解,二叉树问题

目录

今日知识点:
对于每个子树如果和小于0就返回0;如果大于0就直接返回。

注意异或的性质,偶消奇不消,所以lca上面的都消掉了,并不需要跑lca,也就是说只需要把根到所有点的距离跑出来即可

如果上传过来小于k个,我们是考虑把当前根节点也加入其中;如果大于k,那就直接返回失败;等于k忽略

这棵树的深度就是这棵树上到根节点的最长距离+1;这棵树的宽度就是到根节点距离相同的节点个数的最大值

最大子树和

思路: 

树上异或

思路: 

树的分解

思路: 

二叉树问题

思路: 


        

        

最大子树和

思路: 

对于每个子树:如果子树和小于0,直接丢掉吧,所以返回0;如果大于0就直接返回。

#include <bits/stdc++.h>
using namespace std;
const int N=2e4;
typedef long long ll; 
vector<int>ve[N];
ll ans,n,a[N],f[N],INF=-1e11;
int dfs(int u,int fa){
	for(int i=0,sz=ve[u].size();i<sz;i++){
		int v=ve[u][i];
		if(v==fa)continue;
		f[u]+=dfs(v,u);
	}
	f[u]+=a[u];
	ans=max(f[u],ans);
	if(f[u]<0)return 0;
	else return f[u];
}
int main(){
	cin>>n;int x,y;ans=INF;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n-1;i++){
		cin>>x>>y;
		ve[x].push_back(y);
		ve[y].push_back(x);
	}
	dfs(1,-1);
	cout<<ans;
}

        

        

树上异或

思路: 

注意异或的性质,偶消奇不消,所以lca上面的都消掉了,并不需要跑lca(你喜欢写LCA代码吗?)也就是说只需要把根到所有点的距离跑出来即可

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int tot,n,m,head[N],f[N];
struct node{int to,w,next;}e[N*2];
void add(int u,int v,int w){e[++tot]={v,w,head[u]};head[u]=tot;}
void dfs(int u,int fa,int num){
	f[u]=num;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to,w=e[i].w;
		if(fa==v)continue;
		dfs(v,u,num^w);
	}
}
int main(){
	cin>>n;int u,v,w;
	for(int i=1;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);add(u,v,w);add(v,u,w);	
	}
	dfs(1,0,0);
	cin>>m;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		printf("%d\n",f[u]^f[v]);	
	}
	return 0;
}

        

        

树的分解

思路: 


此题不好做,首先要明白最终的效果,必然是k个联通的,那么对于每个节点来说,如果孩子匹配不成功,是可以和父亲继续匹配的,也就是上传当前个数即可。

那么对于节点来说:一定是和上传过来的未匹配成功的孩子都保持一个颜色,
否则就联通不了,你可以画图证明。(因为如果匹配成功就可以忽略)

那么如果上传过来小于k个,我们是考虑把当前根节点也加入其中;如果大于k,那就直接返回失败;等于k忽略

#include <bits/stdc++.h>
using namespace std;
int n,k,t,sum;
vector<int>ve[100005];
int dfs(int u,int fa){
	int ans=1;//初始化上传数
	for(int i=0,sz=ve[u].size();i<sz;i++){
		if(ve[u][i]==fa)continue;
		int a=dfs(ve[u][i],u);
		if(a==-1||a>k)return -1;//这段还认识吗,高速公路哦
		else if(a==k)continue;//直接上传0
		ans+=a;//更新上传数
	}
	return ans;
}
int main(){
	cin>>t;
	while(t--){
		cin>>n>>k;int a,b;
		for(int i=0;i<=n;i++)ve[i].clear();
		for(int i=1;i<n;i++){
			cin>>a>>b;
			ve[a].push_back(b);
			ve[b].push_back(a);
		}
		int ans=dfs(1,1);
		if(ans==k)cout<<"YES\n";//包括根节点在内仅上传k个则代表成功分解
		else cout<<"NO\n";
	}
	return 0;
}

        

        

二叉树问题

思路: 

树的深度好说,宽度很容易让人想到树的直径(【算法每日一练]-图论(保姆级教程篇16 树的重心 树的直径)#树的直径 #会议 #医院设置-CSDN博客)感兴趣可以看看啊!

但是!!!这俩玩意不是一个东西哈,直径是直径,宽度是宽度。

现在要求查询这棵树的深度,其实就是这棵树上到根节点的最长距离+1;查询这棵树的宽度,其实就是到根节点距离相同的节点个数的最大值

要处理第3个问题,一个很巧妙的做法是直接就把指向叶子方向的边权设为1,指向根方向的边权设为2。然后最短路即可

#include <bits/stdc++.h>
using namespace std;//拿图
const int N=200;
int n,ans,tot,dis[N],vis[N],head[N],tmp[2000];
struct node{int to,w,next;}e[N*2];
void add(int u,int v,int w){e[++tot]={v,w,head[u]};head[u]=tot;}
void spfa(int s){
	queue<int>q;q.push(s);
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	vis[s]=1;dis[s]=0;
	while(!q.empty()){
		int u=q.front();q.pop();vis[u]=0;
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to,w=e[i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				if(!vis[v]){vis[v]=1;q.push(v);}
			}
		}
	}
}
int main(){
	cin>>n;int u,v;
	for(int i=1;i<n;i++){//树是n点n-1边
		cin>>u>>v;add(u,v,1);add(v,u,2);
	}
	cin>>u>>v;spfa(1);
	for(int i=1;i<=n;i++)tmp[dis[i]]++,ans=max(ans,dis[i]);
	cout<<ans+1<<'\n';ans=0;
	for(int i=1;i<=2000;i++)ans=max(ans,tmp[i]);
	cout<<ans<<'\n';
	spfa(u);cout<<dis[v]<<'\n';
}

  • 34
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值