• 换根DP


    换根(DP)属于树形(DP),是二次扫描,因为这类问题通常不会指定根结点,并且根节点的变化会对一些值,例如子结点深度和、点权和等产生影响。
    通常需要二次(DFS),第一次(DFS)来处理深度,以及点权和之类的问题,第二次(DFS)开始动态规划。

    (u)为当前结点,(v)为当前结点的叶子结点。首先需要用(s_i)来表示以(i)为根的子树中的结点个数,并且有(s_u = 1 + sum s_v),那么这个通过一遍(DFS)就可以办得到。

    考虑状态转移,令(f_u)表示以(u)为根时,所有结点深度之和,那么进行换根,让(v)为根,那么转移就是(f_v <- f_u),那么这时候各结点的深度可以分为一下两种情况:

    • 所有在(v)的子树上的结点深度都减少(1),那么总深度之和就减少(s_v)
    • 所有不在(v)的子树上的结点深度都增加(1),那么总深度和就增加(n - s_v)
      那么状态转移方程就可以推出来了,(f_v = f_u - s_v + n - s_v = f_u + n - 2 * s_v)
      于是第二遍(DFS)处理这个东西就好,这样就能求出以所有结点为根节点的深度和了。

    例题:[POI2008]STA-Station
    代码:

    // Problem: P3478 [POI2008]STA-Station
    // Contest: Luogu
    // URL: https://www.luogu.com.cn/problem/P3478
    // Memory Limit: 125 MB
    // Time Limit: 2000 ms
    // 
    // Powered by CP Editor (https://cpeditor.org)
    
    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1E6 + 10, M = N * 2;
    
    int Size[N];
    int h[N], e[M], ne[M], idx;
    int depth[N];
    long long f[N];
    int n;
    
    void add(int a, int b) {
    	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    int dfs1(int u, int fa) {
    	Size[u] = 1;
    	if (fa == -1) depth[u] = 0;
    	else depth[u] = depth[fa] + 1;
    	
    	for (int i = h[u]; ~i; i = ne[i]) {
    		int j = e[i];
    		if (j == fa) continue;
    		int S = dfs1(j, u);
    		Size[u] += S;
    	}
    	
    	return Size[u];
    }
    
    void dfs2(int u, int fa) {
    	for (int i = h[u]; i != -1; i = ne[i]) {
    		int v = e[i];
    		if (v == fa) continue;
    		f[v] = f[u] - 2 * Size[v] + n;
    		dfs2(v, u);	
    	}
    }
    
    int main() {
    	scanf("%d", &n);
    	memset(h, -1, sizeof h);
    	for (int i = 1; i <= n - 1; i++) {
    		int a, b;
    		scanf("%d%d", &a, &b);
    		add(a, b), add(b, a);	
    	}
    	
    	dfs1(1, -1);
    	
    	for (int i = 1; i <= n; i++) f[1] += depth[i];
    	
    	//换根
    	//从u->v,在v子树上的深度减一,一共减去Size[v]
    	//不在v子树上的,深度都增加1,总共增加n - Size[v]
    	//那么从u到v答案变化,f[v] = f[u] - Size[v] + n - Size[v] = f[u] - 2 * Size[v] + n
    	dfs2(1, -1);
    	
    	int res = 1;
    	long long cnt = 0;
    	for (int i = 1; i <= n; i++) {
    		if (f[i] > cnt) {
    			cnt = f[i];
    			res = i;
    		}
    	}
    	
    	printf("%d
    ", res);
    	
        return 0;
    }
    
  • 相关阅读:
    Java集合框架知多少——干货!!!
    Java基础小记
    初识Java
    HTML5入门必知
    密码技术小结
    [MDK]Keil在下载程序一直提示更新J-Link
    Python 循环
    2021年7月14日
    bzoj 2653 middle (主席树+二分)
    bzoj 3932 [CQOI2015]任务查询系统 (主席树)
  • 原文地址:https://www.cnblogs.com/ZhengLijie/p/15357762.html
Copyright © 2020-2023  润新知