首页 > 其他分享 >2023牛客暑期多校练营6 A-Tree 树上背包+并查集

2023牛客暑期多校练营6 A-Tree 树上背包+并查集

时间:2023-08-28 13:12:11浏览次数:45  
标签:sz 子树 const int 边权 查集 Tree 多校 黑点

2023牛客暑期多校练营6 A-Tree 树上背包+并查集

题目链接

题意:

 给出一棵树,节点为黑色或者白色,定义整棵树的贡献为,任意白点到任意黑点所经过路径上的最大边权之和,节点i原本颜色已给出,可以花费c[i]代价翻转节点i的颜色,问最大贡献是多少。

做法:

首先我们思考怎么处理最大边权的问题,怎么去确定某条路径上的最大边权。

  • 答案是类似kruscal的处理办法,我们可以将边权排序,从小到大枚举,用并查集来维护两颗子树
  • 设u,v为当前枚举到边的两端,即以u为根的子树和以v为根的子树,因为是从小到大枚举,u,v中不会存在比当前边更大的边,那么这条边的贡献即为: 边权*(子树u中白点数量*子树v中黑点数量+子树u中黑点数量*子树v中白点数量)
  • 在计算贡献时我们只需要知道子树的大小,不需要知道树长什么样,因此直接并查集维护大小即可。

接下来就是常规的优化树上背包,定义状态dp[i][j],i表示连通块的编号,j表示有几个黑点,转移方程如下,其中tmp为临时数组

    ll val  = w*(j*(sz[v]-k)+k*(sz[u]-j));
    //j为u中黑点数,k为v中黑点数,sz表示子树大小
    tmp[j+k] = max(tmp[j+k],dp[u][j]+dp[v][k]+val);

总的来说这题还是非常秒的,但想明白并查集后就非常简单了。

代码:

#define fst std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout << std::fixed << std::setprecision(20)
#define le "\n"
#define ll long long 
#include <bits/stdc++.h>
using namespace std;
const int N=3000+50;
const int mod=998244353;
int col[N],cost[N],sz[N];
ll dp[N][N];
int fa[N];
struct node{
    int u,v;
    ll w;
    bool operator < (const node & nxt) const{
        return w < nxt.w;
    }
};

int find(int x){
    return fa[x]==x ? x: fa[x] = find(fa[x]);
}

int main() {
    int n; cin>>n;
    for(int i=1;i<=n;i++) cin>>col[i];
    for(int i=1;i<=n;i++) cin>>cost[i];
    vector<node> e;    
    for(int i=1;i<n;i++){
        int u,v; ll w;
        cin>>u>>v>>w;
        e.push_back({u,v,w});
    }
    sort(e.begin(),e.end());

    for(int i=1;i<=n;i++){
        sz[i] = 1;
        fa[i] = i;
        dp[i][col[i]] = 0;
        dp[i][col[i]^1] = -cost[i];
    }

    for(auto [u,v,w]: e){
        u = find(u), v = find(v);
        vector<ll> tmp(sz[u]+sz[v]+1,-1e18);
        for(int j=0;j<=sz[u];j++){
            for(int k=0;k<=sz[v];k++){
                ll val  = w*(j*(sz[v]-k)+k*(sz[u]-j));
                tmp[j+k] = max(tmp[j+k],dp[u][j]+dp[v][k]+val);
            }
        }
        fa[v] = u;
        sz[u] += sz[v];
        for(int j=0;j<=sz[u];j++) dp[u][j] = tmp[j];
    }

    int x = find(1);
    cout<<*max_element(dp[x],dp[x]+1+n)<<le;
    return 0;
}

标签:sz,子树,const,int,边权,查集,Tree,多校,黑点
From: https://www.cnblogs.com/touchfishman/p/17662021.html

相关文章

  • 监督学习算法中梯度提升决策树(Gradient Boosting Decision Trees)
    梯度提升决策树(GradientBoostingDecisionTrees,简称GBDT)是一种监督学习算法,它是以决策树为基础分类器的集成学习方法。GBDT通过迭代地训练多个弱分类器(决策树),每个弱分类器都在前一个弱分类器的残差上进行训练,从而逐步减小残差,最终将多个弱分类器组合成一个强分类器。具体而言,GB......
  • 并查集
    将两个集合合并询问两个元素是否在一个集合当中基本原理:每个集合用一棵树表示,树根的编号就是整个集合的编号。每个节点储存它的父节点,p[x]表示x的父节点判断树根(属于那个集合)if(p[x]==x)求x的集合编号:while(p[x]!=x)x=p[x];合并两个集合:px是x的集合编号,py是y的集合......
  • 监督学习算法中决策树(Decision Tree)
    决策树(DecisionTree)是一种常见的监督学习算法,被广泛应用于分类和回归问题中。它通过构建一棵树状结构来对输入数据进行分类或预测。决策树的构建过程基于特征的条件划分,每个内部节点代表一个特征,每个叶子节点代表一个类别或一个数值。决策树的根节点表示整个数据集,通过不断地对数......
  • POJ 1308 Is It A Tree?
    这是我做出来的第一道有含量的ACM题,应该好好总结一下!这道1308的题,其实很简单,只要抓住了树的特征,就可以解出来。我的解法的思想是这样的:树的分支数m和树的结点数n有一个关系:n==m+1,只要抓住了这个特征,问题便可迎刃而解! 源代码:#include<stdio.h>#include<cstring>inta,b,m=0,n=0,k=......
  • 并查集
    2023.8.26很晚了,还来得及吗 2023-08-2621:18:03P2661[NOIP2015提高组]信息传递-洛谷|计算机科学教育新生态(luogu.com.cn)还未完成 2023-08-2708:21:06已完成  初步总结:主要分为三个模块:合并,查找,移动(还不会)注意:合并时,是把根节点合并,而非自身优化:r......
  • 【AL&MT】Decision Tree
    1Introductionusualclassindecisiontree:ID3,C4.5,CARTID3:/InformattionEntropy,基于信息熵和信息增益C4.5:/信息增益率,baseontheID3CART:/基尼系数,usingregressorclass2achieving1.1ID3decisiontreeD-trainingset,a-attribut......
  • CF1858D Trees and Segments
    一道考查预处理技巧的dp。观察式子\(a\timesL_0+L_1\),一个显然的想法是“定一求一”,即预处理求出对于每个\(L_1\)最大的\(L_0\),然后对于每个\(a\),枚举\(L_1\),统计最大的\(a\timesL_0+L_1\)。这样,我们将问题转化为了:已知\(L_1=len\),求出\(dp_{len}=L_{0max}\)。dp数......
  • CodeForces 825G Tree Queries
    洛谷传送门CF传送门模拟赛赛时做法。看到查询路径点权最小值,想到建重构树,满足重构树上\(\operatorname{LCA}(x,y)\)为原树上\(x\toy\)路径的点权最小值。建树方法可以参考CF1797FLiHuaandPath。于是问题变成了,维护一个点集,支持加点,查询给定点\(x\)到点集中所有......
  • [CF1794E] Labeling the Tree with Distances 题解
    [CF1794E]LabelingtheTreewithDistances题解题目描述给你一个树,边权为\(1\)。给定\(n-1\)个数,你需要将这些数分配到\(n-1\)个节点上。一个点\(x\)是好的,当且仅当存在一种分配方案,所有被分配数的点到\(x\)的最短路径长度等于其被分配的数。求所有好点。思路从......
  • P3521 [POI2011] ROT-Tree Rotations
    P3521[POI2011]ROT-TreeRotations首先合并两棵子树的时候只关心子树内值的个数,并不关心子树内具体是什么顺序,引导从下向上线段树合并计算代价。每一个值只会出现一次,首先每个叶子节点开一棵动态开点值域为\(1-n\)的线段树维护,初始只有自己的值的位置为\(1\)。然后对于每......