给出一个\(N\)个点的树,找出一个点来,以这个点为根的树时,全部点的深度之和最大spa
看到树,还让求最大,这种可能不是贪心就是树形\(DP\),贪心的话树的形状无法判断,果断放弃,那么就只能是\(DP\)了。
既然它让求深度之和,那么我就直接定义以\(i\)为根时深度和为\(DP_i\),接下来就是怎么转移的问题了。若是我枚举每一个点来考虑,那么还要计算它下边的子树和它上边的子树,显然是很差弄,时间复杂度可能在\(O(N^2)\)左右,虽然时间十秒但也不够用啊,因为\(n\)大到了1000000,因此这个题仍是得用\(O(n)\)的效率,若是我以某种手段获得了\(DP_1\),那么接下来的转移就好说了,每次往下找一个儿子\(v\),深度减少了\(siz_v\),增长了\(n-siz_v\),这样就能用两个\(O(n)\)来完成这个题,最后在\(O(n)\)的统计一下答案就好。code
#include<cstdio> using namespace std; const int N=1e6+10; struct Edge{ int to,nxt; }e[N<<1]; int dep[N],Head[N],len; void Ins(int a,int b){ e[++len].to=b;e[len].nxt=Head[a];Head[a]=len; } int dp[N],siz[N]; void dfs(int u,int fa){ siz[u]=1;dp[u]=dep[u]; for(int i=Head[u];i;i=e[i].nxt){ int v=e[i].nxt; if(v==fa)continue; dep[v]=dep[u]+1; dfs(v,u); siz[u]+=siz[v]; dp[u]+=dp[v]; } } int n; void calc(int u,int fa){ for(int i=Head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa)continue; dp[v]=dp[u]-siz[v]+n-siz[v]; calc(v,u); } } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ int a,b; scanf("%d%d",&a,&b); Ins(a,b);Ins(b,a); } dfs(1,0); calc(1,0); int ans=0; for(int i=1;i<=n;i++) if(dp[ans]<dp[i])ans=i; printf("%d\n",ans); }
转移方程题目问什么设什么先,求不出来再考虑换或者辅助一下\(DP\).io