Hdu 5915 The Fastest Runner Ms. Zhang(环套树)

题意:
有一个n个点n条边的树,确定S,T,使得从S出发经过所有点最终到T,记长度为t,使得(t,S,T)的字典序最小


思路:
环套树,破环成树
1.起点终点在同一个子树上,[S,T]上的边只走一次,环上的边只走一次

2.起点和终点在不同的子树上,起点到U,终点到V只走一遍,环上U,V之间只走一遍,其余的边都走两遍
设置一个环上的起点,其余的点到这个点的距离设为dis[x]
则长度为2*n-2+dis[u]-(S,u)-dis[v]-(T,v)或者为2*n-2-circle-dis[u]+dis[v]-(S,u)-(T,v)
分别维护dis[u]-(S,u)的最小值以及-dis[u]-(S,u)的最小值就可以了

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
struct Edge{
    int to,next;
}e[N*2];
int head[N],tot,fa[N],vis[N],circle_len;
vector<int>mp;
bool Del[N];

void init(){
    tot=0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(Del,false,sizeof(Del));
}

void addedge(int from,int to){
    e[tot]=(Edge){to,head[from]};
    head[from]=tot++;
}

bool get_circle(int u){
    vis[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(v==fa[u])    continue;
        if(vis[v]){
            int x=u;
            circle_len=1,mp.push_back(u),Del[u]=true;
            while(x!=v) x=fa[x],circle_len++,mp.push_back(x),Del[x]=true;
            return true;
        }
        fa[v]=u;
        if(get_circle(v))   return true;
    }
    return false;
}

int id[N],mx[N];
struct node{
    int maxv,id1,id2;
}a[N],ans;

bool check(int x1,int x2,int y1,int y2){
    if(x1<y1||(x1==y1&&x2<y2))  return true;
    return false;
}

void dfs(int u,int pre,int Belong){
    mx[u]=0,id[u]=u;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(Del[v]||v==pre)    continue;
        dfs(v,u,Belong);
        int tmp=mx[v]+1,tmp_id1=id[v],tmp_id2=id[u];
        if(tmp_id1>tmp_id2) swap(tmp_id1,tmp_id2);
        if(mx[u]+tmp>a[Belong].maxv)
            a[Belong].maxv=mx[u]+tmp,a[Belong].id1=tmp_id1,a[Belong].id2=tmp_id2;
        else if(mx[u]+tmp==a[Belong].maxv&&check(tmp_id1,tmp_id2,a[Belong].id1,a[Belong].id2))
            a[Belong].id1=tmp_id1,a[Belong].id2=tmp_id2;
        if(mx[u]<tmp||(mx[u]==tmp&&id[u]>id[v]))   mx[u]=tmp,id[u]=id[v];
    }
}

int dis[N],n;

//维护dis[u]-(S,u)的最小值以及-dis[u]-(S,u)的最小值就可以了
void solve(int flag){
    dis[0]=0;
    int tmp_len=-mx[mp[0]],ID=id[mp[0]];
    for(int i=1;i<mp.size();i++){
        dis[i]=dis[i-1]+1;
        int tmp=2*n-2+tmp_len-flag*dis[i]-mx[mp[i]],tmp_id1=id[mp[i]],tmp_id2=ID;
        if(flag==-1)    tmp-=circle_len;
        if(tmp_id1>tmp_id2) swap(tmp_id1,tmp_id2);
        if(tmp<ans.maxv||(tmp==ans.maxv&&check(tmp_id1,tmp_id2,ans.id1,ans.id2)))    ans.maxv=tmp,ans.id1=tmp_id1,ans.id2=tmp_id2;
        if(flag*dis[i]-mx[mp[i]]<tmp_len||(flag*dis[i]-mx[mp[i]]==tmp_len&&id[mp[i]]<ID))
            ID=id[mp[i]],tmp_len=flag*dis[i]-mx[mp[i]];
    }
}

int main(){
    int _;
    scanf("%d",&_);
    for(int Case=1;Case<=_;Case++){
        scanf("%d",&n);
        int u,v;
        init(),mp.clear();
        for(int i=1;i<=n;i++){
            scanf("%d%d",&u,&v);
            addedge(u,v),addedge(v,u);
        }
        fa[1]=0;
        get_circle(1);
        ans.maxv=1e6;
        for(int i=1;i<=n;i++)
            if(Del[i]){
                a[i].maxv=0,dfs(i,0,i);
                int tmp=2*n-circle_len-a[i].maxv;
                if(tmp<ans.maxv||(tmp==ans.maxv&&check(a[i].id1,a[i].id2,ans.id1,ans.id2)) )    ans=a[i],ans.maxv=tmp;
            }
        solve(1);
        solve(-1);
        printf("Case #%d: %d %d %d\n",Case,ans.maxv,ans.id1,ans.id2);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值