4 solutions

  • 8
    @ 2025-1-16 16:34:53

    本题解寻找重心和统计路径均使用DFS

    #include<iostream>
    #include<vector>
    using namespace std;
    int n,max_[1000005],size_[100005],cen=0,ssum=0;//max_[u]储存节点u的最大子树,size_[u]储存节点u及以下的子树大小
    vector<int>g[1000005];
    
    void addedge(int u,int v){
        g[u].push_back(v);
        g[v].push_back(u);
    }
    
    void findcen(int u,int fa){             //此函数用于寻找重心
        size_[u]=1,max_[u]=0;               //将当前节点作为根节点并进行初始化
        for (auto v:g[u]){
            if (v==fa)continue;
            findcen(v,u);
            size_[u]+=size_[v];
            max_[u]=max(max_[u],size_[v]);  //更新为最大子树的大小
        }
        max_[u]=max(max_[u],n-size_[u]);    //根节点方向子树大小=总大小-其他方向大小,通商进行更新
        if (max_[u]<max_[cen] || (max_[u]==max_[cen] && u<cen)){    
            cen=u;
        }
    }
    
    void getpath(int u,int fa,int sum){     //此函数用于统计总路径长度
        ssum+=sum;                          
        for (auto v:g[u]){
            if (v==fa)continue;
            getpath(v,u,sum+1);
        }
    }
    
    int main()
    {
        max_[0]=(int)1e9;       //注意这里一定要将初始重心的最大子数大小设置为无穷大
        cin >> n;               //否则第21行的判断中会一直认为0节点最优
        for (int i=1;i<n;i++){
            int u,v;
            cin >> u >> v;
            addedge(u,v);
        }
        findcen(1,0);
    
        getpath(cen,0,0);
        cout << cen << " " << ssum;
        return 0;
    }
    
    • @ 2025-1-16 16:35:12

      DFS 永远的神, BFS 根本不配

      #include<iostream>
      #include<vector>
      #define N 50005
      using namespace std;
      int n,c;
      vector<int>g[N];
      int size[N];
      int ma[N];
      void getc(int u,int fa)
      {
      	size[u]=1;
      	ma[u]=0;
      	for(auto v:g[u])
      	{
      		if(v==fa)continue;
      		getc(v,u);
      		size[u]+=size[v];
      		ma[u]=max(ma[u],size[v]);
      	}
      	ma[u]=max(ma[u],n-size[u]);
      	if(c==0 || ma[u]<ma[c] || (ma[u]==ma[c] && u<c))
      		c=u;
      }
      int dfs(int u,int fa,int deep)
      {
      	int sum=deep;
      	for(auto v:g[u])
      	{
      		if(v==fa)continue;
      		sum+=dfs(v,u,deep+1);
      	}
      	return sum;
      }
      int main()
      {
      	cin>>n;
      	for(int i=1;i<n;i++)
      	{
      		int u,v;
      		cin>>u>>v;
      		g[u].push_back(v);
      		g[v].push_back(u);
      	}
      	getc(1,0);
      	cout<<c<<" "<<dfs(c,0,0);
      	return 0;
      }
      
    • @ 2025-1-16 16:44:13

      鸡生体节

      #include<iostream>
      #include<vector>
      #define N 50005
      using namespace std;
      int n,c;
      vector<int>g[N];
      int size[N];
      int ma[N];
      void getc(int u,int fa)
      {
      	size[u]=1;//初始化只有自己 
      	ma[u]=0;
      	for(auto v:g[u])
      	{
      		if(v==fa)continue;
      		getc(v,u);//递归子结点 
      		size[u]+=size[v];//size[u]指所有子结点的大小 
      		ma[u]=max(ma[u],size[v]);//这个点为重心时,找出最大的子结点大小 
      	}
      	ma[u]=max(ma[u],n-size[u]);//这个点为重心时,父亲结点大小 
      	if(c==0 || ma[u]<ma[c] || (ma[u]==ma[c] && u<c))
      		c=u;
      	/*
      	c==0 的情况是目前没有选出重心,直接沿用 
      	ma[u]<ma[c] 这个就不说了 
      	ma[u]==ma[c] && u<c 这个是判断哪个重心结点编号更小 
      	*/
      }
      int dfs(int u,int fa,int deep)
      {
      	int sum=deep;
      	for(auto v:g[u])
      	{
      		if(v==fa)continue;
      		sum+=dfs(v,u,deep+1);
      	}
      	return sum;
      }
      int main()
      {
      	cin>>n;
      	for(int i=1;i<n;i++)
      	{
      		int u,v;
      		cin>>u>>v;
      		g[u].push_back(v);
      		g[v].push_back(u);
      	}
      	getc(1,0);
      	cout<<c<<" "<<dfs(c,0,0);
      	return 0;
      }
      
    • @ 2025-1-16 16:49:53

      思路呢?

    • @ 2025-1-16 16:54:53

      #include<bits/stdc++.h> #define pii pair<int,int> using namespace std; vector arr[int(5e4+10)]; int ans[int(5e4+10)],maxx[int(5e4+10)],cnt[int(5e4+10)];int n,anser; void dfs(int u,int f){ for(auto i:arr[u]){ if(if) continue; dfs(i,u); ans[u]+=ans[i]; //cout<<u<<f<<' '<<ans[u]<<'\n'; maxx[u]=max(maxx[u],ans[i]); } maxx[u]=max(maxx[u],n-ans[u]); } struct S{ int u,f,w; }; queue q; void bfs(int x){ q.push({x,0,0}); while(q.size()){ int u=q.front().u,f=q.front().f,w=q.front().w; q.pop(); anser+=w; for(auto i:arr[u]){ if(if) continue; q.push({i,u,w+1}); } } } int main(){ cin>>n; for(int i=1;i<=n-1;i++){ int u,v;cin>>u>>v; arr[u].push_back(v); arr[v].push_back(u); }fill(ans,ans+n+1,1); dfs(1,0); int maxf=1; for(int i=1;i<=n;i++) if(maxx[maxf]>maxx[i]) maxf=i; bfs(maxf); cout<<maxf<<' '<<anser; return 0; }

    • @ 2025-1-16 16:55:22

      题意 找树的“中心”,使每个结点到他的距离加起来(距离和)最小。

      思路 这个“中心”其实是重心。重心的性质[1]有一个就是“树中所有点到某个点的距离和中,到重心的距离和最小”。

      所以我们要求的就是重心,如何求重心呢?

      利用重心的另一个性质:以重心为树根时,所有子树的大小不超过全树大小的一半。根据这个性质我们可以用 DFS 求出来重心。注意最大子树大小最小(理解不了看代码)的结点才是重心,因为小于全树大小一半在非重心也有可能做到。

      代码 #include <bits/stdc++.h> using namespace std; const int N = 5e4 + 5; vector a[N]; int n,root; // 结点个数;重心 int sz[N],stsz[N] = {0x7fffffff}; // 树的大小;以 i 为根结点时最大子树的大小 int dis[N],sum; // 每个点到重心的距离;距离和 // 找重心 void dfs(int u,int f){ sz[u] = 1; for (int v : a[u]){ if (v != f){ dfs(v,u); sz[u] += sz[v]; stsz[u] = max(stsz[u],sz[v]); } } stsz[u] = max(stsz[u],n - sz[u]); // 父节点的子树 if (stsz[u] < stsz[root] || stsz[u] == stsz[root] && u < root) root = u; } // 计算每个点到重心的距离 void bfs(int root){ queue<pair<int,int>> q; q.push({root,0}); while (!q.empty()){ int u = q.front().first,f = q.front().second; q.pop(); for (int v : a[u]){ if (v != f){ dis[v] = dis[u] + 1; sum += dis[v]; q.push({v,u}); } } } } int main(int argc, char **argv){ cin >> n; for (int i = 1;i < n;i++){ int u,v; cin >> u >> v; a[u].push_back(v); a[v].push_back(u); } dfs(1,0); // 找重心 bfs(root); // 计算每个点到重心的距离 cout << root << ' ' << sum; return 0; } 重心的四个性质:1. ↩︎

  • 5
    @ 2025-1-16 16:32:24

    DFS 永远的神, BFS 根本不配

    #include<iostream>
    #include<vector>
    #define N 50005
    using namespace std;
    int n,c;
    vector<int>g[N];
    int size[N];
    int ma[N];
    void getc(int u,int fa)
    {
    	size[u]=1;//初始化只有自己 
    	ma[u]=0;
    	for(auto v:g[u])
    	{
    		if(v==fa)continue;
    		getc(v,u);//递归子结点 
    		size[u]+=size[v];//size[u]指所有子结点的大小 
    		ma[u]=max(ma[u],size[v]);//这个点为重心时,找出最大的子结点大小 
    	}
    	ma[u]=max(ma[u],n-size[u]);//这个点为重心时,父亲结点大小 
    	if(c==0 || ma[u]<ma[c] || (ma[u]==ma[c] && u<c))
    		c=u;
    	/*
    	c==0 的情况是目前没有选出重心,直接沿用 
    	ma[u]<ma[c] 这个就不说了 
    	ma[u]==ma[c] && u<c 这个是判断哪个重心结点编号更小 
    	*/
    }
    int dfs(int u,int fa,int deep)
    {
    	int sum=deep;
    	for(auto v:g[u])
    	{
    		if(v==fa)continue;
    		sum+=dfs(v,u,deep+1);
    	}
    	return sum;
    }
    int main()
    {
    	cin>>n;
    	for(int i=1;i<n;i++)
    	{
    		int u,v;
    		cin>>u>>v;
    		g[u].push_back(v);
    		g[v].push_back(u);
    	}
    	getc(1,0);
    	cout<<c<<" "<<dfs(c,0,0);
    	return 0;
    }
    
    
  • 4
    @ 2025-1-16 16:49:05

    题意

    找树的“中心”,使每个结点到他的距离加起来(距离和)最小。

    思路

    这个“中心”其实是重心。重心的性质[1]有一个就是“树中所有点到某个点的距离和中,到重心的距离和最小”。

    所以我们要求的就是重心,如何求重心呢?

    利用重心的另一个性质:以重心为树根时,所有子树的大小不超过全树大小的一半。根据这个性质我们可以用 DFS 求出来重心。注意最大子树大小最小(理解不了看代码)的结点才是重心,因为小于全树大小一半在非重心也有可能做到。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 5e4 + 5;
    vector<int> a[N];
    int n,root;	// 结点个数;重心
    int sz[N],stsz[N] = {0x7fffffff};	// 树的大小;以 i 为根结点时最大子树的大小
    int dis[N],sum;	// 每个点到重心的距离;距离和
    // 找重心
    void dfs(int u,int f){
    	sz[u] = 1;
    	for (int v : a[u]){
    		if (v != f){
    			dfs(v,u);
    			sz[u] += sz[v];
    			stsz[u] = max(stsz[u],sz[v]);
    		}
    	}
    	stsz[u] = max(stsz[u],n - sz[u]);	// 父节点的子树
    	if (stsz[u] < stsz[root] || stsz[u] == stsz[root] && u < root)	root = u;
    }
    // 计算每个点到重心的距离
    void bfs(int root){
    	queue<pair<int,int>> q;
    	q.push({root,0});
    	while (!q.empty()){
    		int u = q.front().first,f = q.front().second;
    		q.pop();
    		for (int v : a[u]){
    			if (v != f){
    				dis[v] = dis[u] + 1;
    				sum += dis[v];
    				q.push({v,u});
    			}
    		}
    	}
    }
    int main(int argc, char **argv){
    	cin >> n;
    	for (int i = 1;i < n;i++){
    		int u,v;
    		cin >> u >> v;
    		a[u].push_back(v);
    		a[v].push_back(u);
    	}
    	dfs(1,0);	// 找重心
    	bfs(root);	// 计算每个点到重心的距离
    	cout << root << ' ' << sum;
    	return 0;
    }
    

    1. 重心的四个性质:1. 以重心为树根时,所有子树的大小不超过全树大小的一半;2. 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样;3. 把两棵树通过一条边相连得到一棵新的树,那么新树的重心在连接原来两棵树的重心的路径上;4. 在一棵树上添加或删除一个结点,那么它的重心最多只移动一条边的距离。具体详见《深入浅出 程序设计竞赛(进阶篇)》。 ↩︎

    • -6
      @ 2025-1-16 16:17:13

      呜呜呜,终于吃上题解了

      由于树上的书上的题解一个DFS一个BFS,太不规整了,直接全用DFS!!!!

      #include<bits/stdc++.h>
      using namespace std;
      typedef long long ll;
      vector<ll>edge[100001];
      ll center,n,sum;
      ll len[100001];
      ll pss[100001]={1111111111};
      void lfc(ll num,ll p){
      	len[num]=1;
      	for(auto i:edge[num]){
      		if(i==p)continue;
      		lfc(i,num);
      		len[num]+=len[i];
      		pss[num]=max(pss[num],len[i]);
      	}
      	pss[num]=max(pss[num],n-len[num]);
      	if(pss[center]>pss[num]||(pss[center]==pss[num]&&num<center))center=num;
      }
      void crs(ll num,ll p,ll add){
      	sum+=add;
      	for(auto i:edge[num]){
      		if(i==p)continue;
      		crs(i,num,add+1);
      	}
      }
      int main(){
      	cin>>n;
      	for(ll i=1;i<n;i++){
      		ll u,v;
      		cin>>u>>v;
      		edge[u].push_back(v);
      		edge[v].push_back(u);
      	}
      	lfc(1,1);
      	crs(center,center,0);
      	cout<<center<<" "<<sum;
      }
      
    • 1

    Information

    ID
    317
    Time
    1000ms
    Memory
    125MiB
    Difficulty
    8
    Tags
    # Submissions
    12
    Accepted
    8
    Uploaded By