首页 > 其他分享 >CF1901E Compressed Tree(树dp)

CF1901E Compressed Tree(树dp)

时间:2023-11-28 09:44:26浏览次数:31  
标签:ch int Tree son Compressed 答案 ans dp

Problem

题目地址

Solution

来自fcy大佬的思路

记 \(f_u\) 表示假定以 \(u\) 为根的子树,在压缩后,(子树内的某一个点(包括 \(u\)))可以向外(除\(u\)为根的子树外所以点的集合)连一条边时的最大 \(sum\)。换言之,我们把树拆成 以\(u\)为根的子树(记作 \(Tree_u\)) 和 非 \(Tree_u\) 部分。而且,假定 \(Tree_u\) 部分在压缩后一定有向外的边(可以是 \(u\) 连出去的,也可以是 \(u\) 被压缩后 \(u\) 子树内的某一个点连出去的),连向非 \(Tree_u\) 部分。

假设当前结点为 \(u\) ,考虑 \(f_u\) 的状态转移,记 \(u\) 的儿子为 \(v_1,v_2,...,v_m\),那么有三种转移情况:一、取0个儿子,\(a_u\) 就是 \(f_u\);二、取1个儿子,那么 \(u\) 会被压缩, \(a_u\) 不加入 \(f_u\) 的计算;三、取2个或2个以上的儿子,\(u\) 不会被压缩,\(a_u\) 要加入 \(f_u\) 的计算中。

考虑答案的计算。假定 \(1\) 为根,此时,根不能向外连边,这是与 \(f_u\) 的定义不同的。但是,我们可以仿照 \(f_u\) 的状态转移来计算答案。具体的,分为 种:一、取0个儿子,此时 \(a_1\) 就是答案;二、取1个儿子,\(1\) 是“叶子”结点,\(a_1\) 加入到答案的计算;三、取2个儿子,\(1\) 会被压缩,\(a_1\) 不加入答案的计算;四、取2个以上的儿子,\(1\) 不会被压缩,\(a_1\) 加入答案的计算。

考虑到 \(f_u\) 的意义,\(Tree_u\) 在压缩后可以向 非 \(Tree_u\) 部分 连边当且仅当 非 \(Tree_u\) 部分 不为空集。因此 \(1\) 为根已经考虑任意 \(u\) 的 非 \(Tree_u\) 部分 不为空集的答案。因此,剩余未考虑的答案 有且仅有 对于任意 \(u\),非 \(Tree_u\) 部分 为空集的答案。事实上,对于任意 \(u\),像 \(1\) 一样计算答案即可(相当于把 \(Tree_u\) 当成“整棵树”)。

最后的 \(\max\) 便是答案

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int INF = 1e18;
const int N = 5e5+7;
int n,cnt;
int a[N],head[N],f[N];
struct Edge {
    int next, to;
}edge[N<<1];
void add(int u,int v) {
    edge[++cnt] = (Edge)<%head[u], v%>;
    head[u] = cnt;
}
bool cmp(int x, int y) {
    return x > y;
}
int ans;
void Dfs(int u,int fa) {
    f[u] = a[u], ans = max(ans, a[u]);
    vector<int> son;
    for(int i=head[u];i;i=edge[i].next) {
        int v = edge[i].to;
        if(v == fa) continue;
        Dfs(v, u), son.emplace_back(f[v]);
    }
    if(!son.size()) return ;
    sort(son.begin(), son.end(), cmp);
    f[u] = max(f[u], son[0]);
    int sum = son[0];
    for(int i=1;i<son.size();++i) {
        sum += son[i];
        f[u] = max(f[u], sum+a[u]);
    	if(son[i] <= 0) break;
    }
    if(son.size() >= 1) ans = max(ans, son[0]+a[u]);
    if(son.size() >= 2) ans = max(ans, son[0]+son[1]);
    if(son.size() > 2) {
        sum = son[0] + son[1];
        for(int i=2;i<son.size();++i) {
            sum += son[i];
            ans = max(ans, sum+a[u]);
        	if(son[i] <= 0) break;
        }
    }
}
void solve() {
    n = read();
    for(int i=1;i<=n;++i) a[i] = read();
    for(int i=1;i<n;++i) {
        int x = read(), y = read();
        add(x, y), add(y, x);
    }
    ans = 0;
    Dfs(1, 0);
    printf("%lld\n",ans);
	for(int i=1;i<=n;++i) a[i] = head[i] = f[i] = 0;
    cnt = 0;
}
signed main()
{
    int T = read();
    while(T--) {
        solve();
    }
	return 0;
}

Summary

有时候题目哪里限制我们让我们不好dp,我们就假定一个条件让它好dp。

标签:ch,int,Tree,son,Compressed,答案,ans,dp
From: https://www.cnblogs.com/BaseAI/p/17861159.html

相关文章

  • DP2
    DP2UVA12141LineChart先离散化一波,记位置从小到大第\(i\)个元素离散化后的大小为\(a_i\)。这题最大的难点就在于如何避免计重。如果现在要更新\(i\)位置的dp值,且\(\existsp<q,a_p=a_q\neqa_i\),则贪心地考虑用\(q\)转移,而不是\(p\),因为\(q\)位置结尾包......
  • AtcoderDP1
    AtcoderDP1收录非计数dp题。[ABC227F]TreasureHunting(2323)Problem给你一个\(N\timesM\)的矩阵,你需要从坐标\((1,1)\)走到坐标\((N,M)\)去,每次只能向右或者向下走。坐标\((i,j)\)的价值是\(A_{i,j}\)。我们定义一条路径的价值是,这条路径经过的坐标的前\(K\)......
  • CodeforcesDP1
    CodeforcesDP1CF833BTheBakery(2200)Problem将一个长度为\(n\)的序列\(a\)分为\(k\)段,使得总价值最大。一段区间的价值表示为区间内不同数字的个数。\(n\leq35000,k\leqmin(n,50),1\lea_i\len\)。Solution记\(f_{i,l,j}\)表示考虑前\(i\)个数,划分成\(......
  • [ABC321E] Complete Binary Tree
    思路:第一次先把往后距离为$k$的点算出来,然后再每次往前走一个,考虑$k-i$的情况。(具体见代码注释)。代码:```cpp#include<bits/stdc++.h>usingnamespacestd;//headintsum[100],head=0;intn,x,k;intans;voidf(intnow,intstep)//从点now开始,往上距离step的点的个数......
  • CF1900 C Anji's Binary Tree 题解
    LinkCF1900CAnji'sBinaryTreeQuestion给出一个树,每个节点上有一个字母L/R/U,从\(1\)号根节点开始,L表示接下来走到左节点,R表示接下来走到右节点,U表示接下载走到父节点问,最少修改几个节点上的字母使得从根节点走到叶子节点Solution定义\(F_x\)表示从\(x\)走到叶......
  • 两个大小相同集合最接近的累加和 -dp
    给定一个正数数组arr,请把arr中所有的数分成两个集合如果arr长度为偶数,两个集合包含数的个数要一样多如果arr长度为奇数,两个集合包含数的个数必须只差一个请尽量让两个集合的累加和接近返回最接近的情况下,较小集合的累加和字节面试​暴力递归 publicstaticintright(int......
  • 随手写了个博客多平台发布脚本:Python自动发布文章到Wordpress
    引言作为一名技术博主,提高博客发布效率是我们始终追求的目标。在这篇文章中,我将分享一个基于Python的脚本,能够实现博客多平台发布,具体来说,是自动发布文章到WordPress。通过这个简单而高效的脚本,我们能够省去繁琐的手动发布步骤,提升工作效率。技术栈在编写这个自动发布脚本的过......
  • Top Tree
    总归要学的。先写一下理解比较困难的点。考虑SATT的建立过程:首先在树里面找到一个CompressTree,这个树满足中序遍历写下来是根簇路径深度从小到大排列,然后根簇路径上挂着的小簇Rake起来,这个Rake的过程是容易的,考虑对于每一个直接连接的小簇,把他变成子问题,然后给一个代表点(R......
  • 通用串口modbus转PROFIBUS DP网关PM-160在汽车行业的应用案例
    通用串口modbus转PROFIBUSDP网关PM-160在汽车行业的应用案例摘要:PM-160是泗博公司生产的,可以实现串口与PROFIBUSDP协议数据通信的网关。此案例讲述的是通过PM-160网关,成功将梅特勒-托利多电子秤上的自定义协议数据传递给西门子PLC的应用案例说明。背景:某公司做轴承和汽......
  • 2023 合肥站 热身赛 B Problem F. Flower’s Land 换根dp 依赖背包
    传送门。求出包含某个点连通块大小为K的权值和最大值。钦定1为根节点,只求根节点的答案,其实是一个依赖性01背包问题可以\(nk\)的时间内解决。考虑进行换根操作,由于背包是取max的背包没办法进行背包的删除,然而取前后缀背包背包的合并为\(k^2\)复杂度过高。当时还有一个想法是点......