• P1395 会议 题解


    CSDN同步

    原题链接

    简要题意:

    给定 (n) 个节点的一棵树,用 ( ext{dis}(x,y)) 表示 (x,y) 的距离,(s_x = sum_{y=1}^n ext{dis}(x,y)).求最小的 (x)(s_x).(其实就是求树上到一点距离之和最小的点和该距离)

    (n leq 5 imes 10^4).

    数据加强:(n leq 10^7).

    首先,我们可以统计 (a_x) 表示以 (x) 为根的子树大小。我们开局默认 (1) 是整棵树的根,那么树就有了层次,子树就是唯一的。

    如何快速统计 (s_x) 呢?你会发现,如果用 ({fa}_x) 表示 (x) 的父亲节点,那么 (s_x)(s_{{fa}_x}) 是可以进行转移的。这是 典型的换根 ( ext{dp}) 模板。

    简单来说,就是,已知 (s_{{fa}_x}),如何 (mathcal{O}(1)) 求出 (s_x) 呢?

    实际答案不难:

    • (s_{{fa}_x}) 的基础上转到 (s_x),则以 (x) 为根的子树对 (x) 的贡献比对 ({fa}_x) 的贡献少 (1),所以需要 (-a_x).(包括 (x) 本身)。
    • 不包含在 (x) 为根的子树内的所有节点对 (x) 的贡献比对 ({fa}_x) 的贡献多 (1),所以需要 (+a_1 - a_x).(因为 (1) 是树根,所以 (a_1) 就是所有节点的个数)(这里包括 ({fa}_x) 本身)

    可得:

    [s_x = s_{{fa}_x} - a_x + a_1 - a_x ]

    当然 (s_1) 我们可以在统计 (a) 的时候顺便暴力算一下。

    时间复杂度:(mathcal{O}(n)).

    实际得分:(100pts).

    细节:

    如果在统计 (s) 的时候用 miniminh 进行统计可能会出现错误。这是因为,统计 (s) 并不是按照编号大小,而是按照 ( ext{dfs}) 的顺序,题意是 若干 (s_x) 相同的 (x) 输出最小的一个,所以最后我们要重新统计 minh 的答案。

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=5e4+1;
    
    inline int read(){char ch=getchar();int f=1; while(!isdigit(ch)) {if(ch=='-') f=-f; ch=getchar();}
    	   int x=0;while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f;}
    
    int a[N],n,s[N];
    vector<int> G[N];
    int mini,minh;
    
    inline void dfs(int dep,int fa,int far) {
    	bool f=0; s[1]+=far;
    	for(int i=0;i<G[dep].size();i++)
    		if(G[dep][i]-fa) {f=1;break;}
    	if(!f) {a[dep]=1;return;} //叶节点
    	for(int i=0;i<G[dep].size();i++)
    		if(G[dep][i]-fa) {
    			dfs(G[dep][i],dep,far+1);
    			a[dep]+=a[G[dep][i]];
    		} a[dep]++; //别忘了自己
    }
    
    inline void meeting(int x,int fa) {
    	s[x]=s[fa]+a[1]-a[x]-a[x];
    	if(s[x]<mini) mini=s[x],minh=x;
    	for(int i=0;i<G[x].size();i++)
    		if(G[x][i]-fa) meeting(G[x][i],x); //统计 s 和答案
    }
    
    int main(){
    	n=read();
    	for(int i=1;i<n;i++) {
    		int u=read(),v=read();
    		G[u].push_back(v);
    		G[v].push_back(u);
    	} dfs(1,0,0); mini=s[1],minh=1;
    	for(int i=0;i<G[1].size();i++) meeting(G[1][i],1);
    	for(int i=1;i<=n;i++)
    		if(s[i]==mini) {minh=i; break;}
    	printf("%d %d
    ",minh,mini); //更新答案
    	return 0;
    }
    
    
  • 相关阅读:
    Network Flows(借助ortools)
    【转】一张图看懂IaaS, PaaS和SaaS的区别
    论文中的一些符号 O(big-Oh) Ω(big-omega) Θ(big-theta)
    最大流问题
    4 Mininet测量路径的损耗率
    3 Mininet命令延伸实验拓展
    2 Mininet可视化应用
    快速定位问题
    软中断与软中断的排查
    系统出现大量不可中断进程与僵尸进程
  • 原文地址:https://www.cnblogs.com/bifanwen/p/13339324.html
Copyright © 2020-2023  润新知