hdu7458 旅行(启发式合并+树形dp)

题目

思路来源

乱搞ac

题解

对于一个u为根的子树,如果出现了这种路径,这个路径要么是(u,v),要么是(v1,v2)

如果是(u,v)的话,得去v子树里刨一个类型相同的点出来

所以,mp[v][x]表示v为子树根,存在一个点p的类型是x,p到v的路径是没用过的最大代价和,用于辅助转移

因为留某个子树不用,肯定是留一个点为了以后用,然后往上启发式合并

合并转移的话,如果用的话是用哪个,再把v往u上挂, 然后考虑u还给不给以后留

而dp[u]表示只考虑u的子树时的最优解,即最大代价和,用于更新答案

更新答案的话,考虑u下有子树v1、v2、v3,

如果v2和v3各存在一条类型是x的路径,就是用dp[v1]+mp[v2][x]+mp[v3][x]更新答案,

用数字表示dp值,用带圈的数字表示mp值,发现

(1+2+③)+(1+②+3)-(1+2+3)=1+②+③,

也就是分成三部分,前两部分是相同结构的,

v3往u上的mp上挂的时候,维护的是v3为子树根,存在一个点p的类型是x,p到v的路径是没用过的最大代价和,也就是③,再加上u下其他子树的dp值之和,也就是1+2,得到1+2+③

而第三部分为u下所有v子树的dp值之和,记为sum

稍稍化简一下,可以得到1+②+③=(1+②+3)-3+③,也就是代码中mp[u][x.fi]+x.se-dp[v]

此外,注意到u的重儿子向u转移的时候,需要全局加上所有轻儿子的dp值之和,

这是一个全局加,所以可以对子树打标记,再魔改下map上维护的值,使之带上子树标记,

于是,now[u]表示对mp[u]上所有点打的全局子树加标记,now[v]表示对mp[v]的

1. 当启发式合并,由于子树大小,需要交换u和v的时候,

此时应该给v上所有点加上sum-dp[v],

但是由于不操作v,所以要操作u上所有点减去这个值,

除此之外,v本身有标记now[v],u本身有标记now[u]

本着不操作v的原则,操作u使得带上now[v]的子树标,

所以应该先令u上所有点加上now[u],

再令u上所有点减去now[v]+sum-dp[v],再把u上点挂到v上

2. 如果不交换的话,就还是把v往u上挂,

把v往u上挂的时候,v需要加上sum-dp[v],加上now[v],再减去now[u]

用map上的值更新答案的时候,也要加上u的子树标和v的子树标,

所以是代码中的now[u]+now[v]+mp[u][x.fi]+x.se-dp[v]

这个全局加,如果用数据结构维护的话会好理解很多,

但是这个做法尚且跑了1s多(map1.7s、umap1.3s),ds感觉已经t飞了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back    
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
namespace fastIO
{
    static char buf[100000],*h=buf,*d=buf;//缓存开大可减少读入时间,看题目给的空间
    #define gc h==d&&(d=(h=buf)+fread(buf,1,100000,stdin),h==d)?EOF:*h++//不能用fread则换成getchar
    template<typename T>
    inline void read(T&x)
    {
        int f = 1;x = 0;
        register char c(gc);
        while(c>'9'||c<'0'){
            if(c == '-') f = -1;
            c=gc;
        }
        while(c<='9'&&c>='0')x=(x<<1)+(x<<3)+(c^48),c=gc;
        x *= f;
    }
    template<typename T>
    void output(T x)
    {
        if(x<0){putchar('-');x=~(x-1);}
        static int s[20],top=0;
        while(x){s[++top]=x%10;x/=10;}
        if(!top)s[++top]=0;
        while(top)putchar(s[top--]+'0');
    }
}
using namespace fastIO;
const int N=2e5+10;
int t,n,c[N],w[N],u,v;
vector<int>e[N];
map<int,ll>mp[N];
ll ans,dp[N],now[N];
void dfs(int u,int fa){
    ll sum=0;
    for(auto &v:e[u]){
        if(v==fa)continue;
        dfs(v,u);
        sum+=dp[v];
    }
    ll pre=0;//pre表示对上一次对mp[u]全局加标记,now表示这一次对mp[u]的全局加标记
    for(auto &v:e[u]){
        if(v==fa)continue;
        if(mp[u].size()<mp[v].size()){
            mp[u].swap(mp[v]);
            for(auto &x:mp[v]){
                if(mp[u].count(x.fi))dp[u]=max(dp[u],now[u]+mp[u][x.fi]+now[v]+x.se-dp[v]);
            }
            pre=now[u];
            now[u]=now[v]+sum-dp[v];
            for(auto &x:mp[v]){
                ll z=x.se+pre-now[u];
                if(mp[u].count(x.fi))mp[u][x.fi]=max(mp[u][x.fi],z);
                else mp[u][x.fi]=z;
            }
        }
        else{
            for(auto &x:mp[v]){
                if(mp[u].count(x.fi))dp[u]=max(dp[u],now[u]+mp[u][x.fi]+now[v]+x.se-dp[v]);
            }
            for(auto &x:mp[v]){
                ll z=x.se+sum-dp[v]-now[u]+now[v];
                if(mp[u].count(x.fi))mp[u][x.fi]=max(mp[u][x.fi],z);
                else mp[u][x.fi]=z;
            }
        }
        mp[v].clear();
    }
    if(mp[u].count(c[u])){
        dp[u]=max(dp[u],now[u]+mp[u][c[u]]+w[u]);
    }
    dp[u]=max(dp[u],sum);
    //printf("u:%d dp:%lld\n",u,dp[u]);
    ll z=sum+w[u]-now[u];
    if(mp[u].count(c[u]))mp[u][c[u]]=max(mp[u][c[u]],z);
    else mp[u][c[u]]=z;
}
int main(){
    read(t);
    while(t--){
        read(n);
        for(int i=1;i<=n;++i){
            e[i].clear();
            dp[i]=now[i]=0;
            mp[i].clear();
            read(c[i]);
        }
        for(int i=1;i<=n;++i)read(w[i]);
        for(int i=2;i<=n;++i){
            read(u);read(v);
            e[u].pb(v);
            e[v].pb(u);
        }
        dfs(1,0);
        ptlle(dp[1]);
    }
    return 0;
}
/*
1
7
3 1 1 2 2 2 3
2 4 1 5 4 6 2
1 2
1 3
2 4
2 5
3 6
3 7
*/

代码转载自:https://pan.quark.cn/s/8ce4326d996e 对于在 CentOS 7 系统中修改网卡配置文件后无法使设置生效的情况,经过实践验证,可以通过使用 nmcli 命令来进行调整。完成修改之后,需要重新启动虚拟机以使更改生效,这样操作流程即告完成。如果设置仍然无法生效,则表明虚拟机在启动过程中所获取的 IP 地址配置并非针对 eth0,此时可以对其它网卡的配置文件进行修改或将其移除。在 CentOS 7 系统中,网络配置的管理机制与早期版本存在差异,主要体现为采用了 Network Manager 服务来负责网络接口的管理。在某些情形下,尽管修改了 `/etc/sysconfig/network-scripts` 目录下的 `ifcfg-eth0` 文件,但网络配置却未能即时生效。此类问题的发生通常源于 CentOS 7 采用了不同于以往的配置读取方法。接下来将具体阐述如何借助 nmcli 命令来处理这一挑战。 以 root 用户身份登录系统并打开终端界面。nmcli 是 Network Manager 提供的命令行界面工具,它支持在命令行环境下执行网络连接的建立、编辑、查询及管理任务。针对修改 eth0 网卡配置的需求,可以遵循以下步骤进行操作: 1. 导航至 `/etc/sysconfig/network-scripts` 目录: ``` cd /etc/sysconfig/network-scripts ``` 2. 检查该目录内是否存在 `ifcfg-eth0.bak` 文件,该备份文件可能是先前调整配置时遗留下来的,若存在可能造成冲突。若发现该文件,可以选择将其删除: ``` [root@localhost netw...
代码转载自:https://pan.quark.cn/s/46fd08fb879c 网管教程 从入门到精通软件篇 ★一。★详尽的xp修复控制台指令及其应用!!! 放入xp(2000)的光盘,安装时选择R,执行修复! Windows XP(涵盖 Windows 2000)的控制台指令是在系统遭遇某些意外状况时的一种极具效用的诊断、检测以及恢复系统功能的工具。笔者确实一直期望能够将这方面的指令进行归纳,此次由老范辛苦整理了这份极具价值的秘籍。 Bootcfg bootcfg 命令用于启动配置与故障恢复(对大多数计算机而言,即 boot.ini 文件)。 带有特定参数的 bootcfg 命令仅在运用故障恢复控制台时方可使用。能够在命令行界面下运用带有不同参数的 bootcfg 命令。 用法: bootcfg /default 设定默认引导选项。 bootcfg /add 向引导清单中增添 Windows 安装。 bootcfg /rebuild 重复整个 Windows 安装流程并让用户选择需添加的项目。 注意:运用 bootcfg /rebuild 之前,应先借助 bootcfg /copy 命令备份 boot.ini 文件。 bootcfg /scan 探查用于 Windows 安装的全部磁盘并展示结果。 注意:这些结果被静态存储,并用于当前会话。若在当前会话期间磁盘配置发生变动,为获取更新的探查结果,必须先重启计算机,然后再次探查磁盘。 bootcfg /list 列示引导清单中已有的项目。 bootcfg /disableredirect 在启动引导程序中禁用重定向。 bootcfg /redirect [ PortBaudRrate] |[ useBio...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值