• 换根DP总结


    洛谷P3478

     

    先来看一道例题,很经典的一道换根DP题。考虑暴力,依次枚举以每一个点为根的深度之和,复杂度是 O(n^2),不能通过此题。这时就需要用换根DP把复杂度降为 O(n)

    我们先进行一次 dfs,假设以 1 为根,把深度之和求出来。

    第二次 dfs 是换根DP的关键。考虑根节点由 u 变成 v,那么 1 号节点和 2 号节点的深度要减一,3 号节点的深度要加一。也就是说,v 子树内的节点深度都要减一,其余的节点深度都要加一。

    于是有转移式 f[v] = f[u] - size[v] + (sum - size[v]),即f[v] = f[u] + sum - 2*size[v]

     一定要注意,先把以 1 号节点为根的总深度求出来,即先求出 f[1],才能进行往后的转移。

    1. #include
    2. #define re register
    3. #define int long long
    4. using namespace std;
    5. inline int read(){
    6. int x=0,f=1;char ch=getchar();
    7. while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
    8. while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48) ; ch=getchar();}
    9. return x*f;
    10. }
    11. const int M = 2e6+10;
    12. int n;
    13. int head[M],siz[M],dep[M],f[M];
    14. int cnt,ans,pos;
    15. struct edge{
    16. int to,nxt;
    17. }e[M];
    18. void add(int u,int v){
    19. e[++cnt].to = v;
    20. e[cnt].nxt = head[u];
    21. head[u] = cnt;
    22. }
    23. void dfs(int u,int fa){
    24. siz[u]=1;
    25. dep[u]=dep[fa]+1;
    26. for(re int i(head[u]) ; i ; i=e[i].nxt){
    27. int v = e[i].to;
    28. if(v == fa) continue;
    29. dfs(v,u);
    30. siz[u]+=siz[v];
    31. }
    32. }
    33. void dfs2(int u,int fa){
    34. for(re int i(head[u]) ; i ; i=e[i].nxt){
    35. int v = e[i].to;
    36. if(v == fa) continue;
    37. f[v] = f[u]+n-2*siz[v];
    38. dfs2(v,u);
    39. }
    40. }
    41. signed main(){
    42. n=read();
    43. for(re int i(1) ; i
    44. int u=read(),v=read();
    45. add(u,v),add(v,u);
    46. }
    47. dfs(1,0);
    48. for(re int i(1) ; i<=n ; ++i) f[1]+=dep[i];
    49. dfs2(1,0);
    50. for(re int i(1) ; i<=n ; ++i) if(ans
    51. printf("%d",pos);
    52. return 0;
    53. }

     

    洛谷P2986

    和上一道题不同的是,每一个点有一个点权,而且每一条边有一条边权。但换根DP的做法是类似的。先以 1 为根节点进行一次 dfs,把每一个点的子树大小求出来,记为 size[i]。同时把 1 号节点的答案处理出来。

    考虑如何处理 1 号节点的答案。v 子树的点的数量 \times w1 + f[v] 和 u子树的点的数量 \times w2 + f[u]。也就是对于 1 号点的所有子树都进行如此操作。f[u] += f[v]+size[v]*e[i].w,在 dfs  的时候往上更新即可。

    然后来看第二个 dfs。同样,从 u 转移到 v 有关系式 f[v] = f[u] - size[v]*e[i].w + (sum-size[v])*e[i].w,所以进行转移即可。最后的答案就是 min(f[i])

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define re register
    7. #define int long long
    8. #define rep(a,b,c) for(re int a(b) ; a<=c ; ++a)
    9. #define drep(a,b,c) for(re int a(b) ; a>=c ; --a)
    10. using namespace std;
    11. inline int read(){
    12. int x=0,f=1;char ch=getchar();
    13. while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
    14. while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48) ; ch=getchar();}
    15. return x*f;
    16. }
    17. const int M = 2e5+10;
    18. int head[M],a[M],siz[M],f[M];
    19. int n;
    20. int cnt,ans=1e15,sum;
    21. struct edge{
    22. int to,nxt,w;
    23. }e[M];
    24. inline void add(int u,int v,int w){
    25. e[++cnt].to = v;
    26. e[cnt].w = w;
    27. e[cnt].nxt = head[u];
    28. head[u] = cnt;
    29. }
    30. inline void dfs1(int u,int fa){
    31. siz[u] = a[u];
    32. for(re int i(head[u]) ; i ; i=e[i].nxt){
    33. int v = e[i].to;
    34. if(v == fa) continue;
    35. dfs1(v,u);
    36. siz[u] += siz[v];
    37. f[u] += f[v]+siz[v]*e[i].w;
    38. }
    39. }
    40. inline void dp(int u,int fa){
    41. for(re int i(head[u]) ; i ; i=e[i].nxt){
    42. int v = e[i].to;
    43. if(v == fa) continue;
    44. f[v] = f[u] - siz[v]*e[i].w + (sum-siz[v])*e[i].w;
    45. dp(v,u);
    46. }
    47. }
    48. signed main(){
    49. n=read();
    50. rep(i,1,n) a[i] = read(),sum += a[i];
    51. rep(i,1,n-1){
    52. int u=read(),v=read(),w=read();
    53. add(u,v,w),add(v,u,w);
    54. }
    55. dfs1(1,0);
    56. dp(1,0);
    57. rep(i,1,n) ans = min(ans,f[i]);
    58. printf("%lld\n",ans);
    59. return 0;
    60. }

    CF1092F 

    和上面那道题基本上一样,把求最小改成求最大就完事了

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define re register
    7. #define int long long
    8. #define rep(a,b,c) for(re int a(b) ; a<=c ; ++a)
    9. #define drep(a,b,c) for(re int a(b) ; a>=c ; --a)
    10. using namespace std;
    11. inline int read(){
    12. int x=0,f=1;char ch=getchar();
    13. while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
    14. while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48) ; ch=getchar();}
    15. return x*f;
    16. }
    17. const int M = 4e5+10;
    18. int head[M],a[M],dep[M],siz[M],f[M];
    19. int cnt,sum;
    20. struct edge{
    21. int to,nxt;
    22. }e[M];
    23. inline void add(int u,int v){
    24. e[++cnt].to = v;
    25. e[cnt].nxt = head[u];
    26. head[u] = cnt;
    27. }
    28. int n;
    29. inline void dfs1(int u,int fa){
    30. siz[u] = a[u];
    31. for(re int i(head[u]) ; i ; i=e[i].nxt){
    32. int v = e[i].to;
    33. if(v == fa) continue;
    34. dep[v] = dep[u] + 1;
    35. dfs1(v,u);
    36. siz[u] += siz[v];
    37. f[u] += f[v] + siz[v];
    38. }
    39. }
    40. inline void dfs2(int u,int fa){
    41. for(re int i(head[u]) ; i ; i=e[i].nxt){
    42. int v = e[i].to;
    43. if(v == fa) continue;
    44. f[v] = f[u] - siz[v] + sum - siz[v];
    45. dfs2(v,u);
    46. }
    47. }
    48. signed main(){
    49. n=read();
    50. rep(i,1,n) a[i] = read(),sum += a[i];
    51. rep(i,1,n-1){
    52. int u=read(),v=read();
    53. add(u,v),add(v,u);
    54. }
    55. dfs1(1,0);
    56. dfs2(1,0);
    57. int ans = 0;
    58. rep(i,1,n) ans = max(ans,f[i]);
    59. printf("%lld\n",ans);
    60. return 0;
    61. }

     

  • 相关阅读:
    zKSync 2.0 新特征解析
    【ARM Trace32(劳特巴赫) 使用介绍 3 - trace32 访问运行时的内存】
    3步就能制作漫画头像的机器人,想拥有一个吗?
    大学生数学建模题论文
    git命令提交代码到远程仓库
    Mac RabbitMq 安装
    Spring Boot配置多个日志文件记录不同类日志示例
    javaScript-事件循环-微任务-宏任务
    软件工程导论——第三章——需求分析
    Oracle 常用命令大全
  • 原文地址:https://blog.csdn.net/glorious_dream/article/details/126341402