树形dp ---- 树形换根dp F - The Maximum Subtree

这篇博客讨论了一种特殊的树——good-tree,其中每个节点对应一个数值区间,边表示区间相交。作者提出了一个解题策略,通过递归和换根DP来寻找树中最大的good-tree子树。在DFS过程中,维护每个节点的最大值和次大值,然后在换根时根据根节点的信息更新子树的选择。最终,通过ACcode展示了如何实现这一算法,解决了超时问题。
摘要由CSDN通过智能技术生成

题目链接


题目大意:

给定一颗树,求这个树的最大子树,且这个子树是一个good-tree。

good-tree的定义是:每个节点可以表示成一个数值区间,而树上的边表示两个点表示的数值区间相交


解题思路:

  1. 根据线段相交,假设以 r o o t root root节点为根,那么我们可以选择两条棵子树进行向下递归继续选择,其他的儿子节点只能选自己,因为线段只有两端向外延伸
  2. 如果不是根节点那么我们只能选一个子树向下延伸,其他都是只能直接选儿子
  3. 那么我们就是要枚举所以点为根去dp,然后更新答案
  4. 但是这样会超时,那么我们就要进行换根dp!
  5. 我们看看要保存什么记录?
  6. 对于每个点我们要记录下面的最大值和次大值的子树,那么我们就选这两个子树进行延伸,其他就直接选。
  7. 换根的时候我们要看看看当前点和根节点之间的关系?
  8. 如果根节点的最大值和次大值里面有一个来着你,那么你就选另一个!
  9. 然后再更新你自己的次大值和最大值就可以了

AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
vector<int> G[maxn];
int dp[maxn], n;
int ans;
pii inf[maxn];
void dfs1(int u, int fa) {
   inf[u] = {0,0};// inf[u].first 记录最大值,second 记录次大值
   for(auto it : G[u]) {
      if(it == fa) continue;
      dfs1(it,u);
      if(dp[it] > inf[u].first) inf[u].second = inf[u].first, inf[u].first = dp[it];
      else if(dp[it] > inf[u].second) inf[u].second = dp[it];
   }
   // max 部分算的是不向下延伸的子树的个数 除了根节点都不加次大值
   dp[u] = max((int)G[u].size()-2,0)+inf[u].first+inf[u].second*(u==1)+1;
}

void dfs2(int u, int fa) {
   if(u != 1) {
      int maxf = (inf[fa].first == dp[u]) ? inf[fa].second : inf[fa].first;
      maxf = maxf + max((int)G[fa].size()-2,0)+1; // fa变成子树的贡献
      // 更新
      if(maxf > inf[u].first) inf[u].second = inf[u].first, inf[u].first = maxf;
      else if(maxf > inf[u].second) inf[u].second = maxf;
      dp[u] = max((int)G[u].size()-2,0)+inf[u].first+inf[u].second+1;
   }
   ans = max(ans,dp[u]);
   for(auto it : G[u]) {
      if(it == fa) continue;
      dfs2(it,u);
   }
}

int main() {
    IOS;
    int T;
    cin >> T;
    while(T --) {
        cin >> n;
        for(int i = 1; i <= n; ++ i) G[i].clear();
        for(int i = 1; i < n; ++ i) {
           int u, v;
           cin >> u >> v;
           G[u].push_back(v);
           G[v].push_back(u);
        }
        dfs1(1,0);
        dfs2(1,0);
        cout << ans << endl;
        ans = 0;
    }   
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值