HDU 5758 Explorer Bo

11 篇文章 0 订阅
10 篇文章 0 订阅

Description

Explorer Bo likes exploring mazes around the world.Now he wants to explore a new maze.
The maze has N rooms connected with N−1 roads of length 1 so that the maze looks like a tree.
Explorer Bo can transfer to a room immediately or walk along a road which is not the one he walked just now.
Because the transfer costs too much, Mr Bo will minimum the transfer using times firstly.
Mr Bo wants to walk along all the roads at least once,but he is lazy and he wants to minimum the total length he walked.
Please help him!
Initial point can be arbitrarily selected

Solution
orz ShinFeb
这个问题比赛的时候想完了70%,剩下30%死也想不到了,orz ShinFeb大爷给出了一个超神的解决方案。
比赛的时候没做出来很愧疚啊。。感觉对不起大家

这题显然dp
那个瞬移次数是可以确定的, =2 (画个图就可以出来,因为一般情况下起点肯定是一个叶子节点,终点也是一个叶子节点)
对于一棵以 v 为根的子树,我们记其叶子节点个数为sz[v]
考虑其一个子节点 to
如果 sz[to]0 mod 2 ,那么 v to的边会被走两遍吧(因为如果叶子节点互相走这条边不会被走到不满足题设)
反之 v to的边只会被走一遍
那么可以直接dp解决。
然而如果叶子节点个数是奇数,那么有一段路径可以省掉,那咋办?
ShinFeb给了一个很好的方案
假设根节点是一个叶子节点。
我们先dp
然后考虑从根节点开始删除一段区间。
对于经过的点,只要把1反转成2,2反转成1即可。
然后求一下可以缩减的最大值即可

Code

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
typedef vector<int> vec;
#define ph push
#define pb push_back
const int M=1e5+5;
vec G[M];
inline void Max(int &a,int b){
    if(a<b)a=b;
}
inline void Min(int &a,int b){
    if(a>b)a=b;
}
inline void rd(int &a){
    a=0;char c;
    while(c=getchar(),!isdigit(c));
    do a=a*10+(c^48);
        while(c=getchar(),isdigit(c));
}
ll dp[M];
int sz[M];
bool mark[M];
void dfs(int v,int f){
    bool flag=0;
    for(int i=0;i<G[v].size();++i){
        int to=G[v][i];
        if(to==f)continue;
        dfs(to,v);
        if(sz[to]&1)++dp[v];
        else dp[v]+=2;
        dp[v]+=dp[to];
        sz[v]+=sz[to];
        flag=1;
    }
    if(!flag)mark[v]=1,sz[v]=1;
}
int Mx=0;
inline void rdfs(int v,int f,int dlt){
    Max(Mx,dlt);
    for(int i=0;i<G[v].size();++i){
        int to=G[v][i];
        if(to^f)rdfs(to,v,dlt+(sz[to]&1?-1:1));
    }
}
inline void gao(){
    int n;cin>>n;
    for(int i=1;i<=n;++i)G[i].clear();
    memset(dp,0,sizeof(dp));
    memset(sz,0,sizeof(sz));
    Mx=0;
    for(int i=1,a,b;i<n;++i){
        rd(a),rd(b);
        G[a].pb(b),G[b].pb(a);
    }
    int s;
    for(int i=1;i<=n;++i)
        if(G[i].size()==1)s=i;
    dfs(s,s);
    rdfs(s,s,0);
    ++sz[s];
    if(sz[s]&1)cout<<dp[s]-Mx<<endl;
    else cout<<dp[s]<<endl;
}
int main(){
    int _;
    for(cin>>_;_--;)gao();
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值