[洛谷P3629][题解][APIO2010]巡逻

0.Description

在一棵树上加1或2条边,新加的边必须走一次。求出从1出发走过所有点回到1的最小路程。

1.Solution

首先可以算出加边前总共要走的路程是 \((n-1)\times2\)
加边操作实际上就是将某一段回溯的路程缩短,由于这个题加的边数非常少,所以我们分类讨论一下。

加一条边

要想让这条边缩短最大的路程,我们可以直接去求树上最长的路径——直径,然后直接减去直径的长度,再加上强制走的这条加边。

加两条边

感性理解可得两条边的第一条一定也是加直径(证明有时间补上)。接下来我们要选剩下的最长路径,但显而易见的是,此时我们选的路径如果和第一条有交集,那么交集部分还要多走一遍,于是我们可以把这些边权都赋值为-1,可以在最后减去路径长度时方便地表示多走一条边,然后就直接求直径即可,此时可以保证选到的边与第一条路径交集最小。注意:由于此时边权有负数,求树的直径时需要用dp。

总结

我们先求出树的直径的长度,记为 \(res_1\) ,如果加一条边则输出 \((n-1)\times2-res_1+1\) 。如果加两条边,则按照上面的方法求出第二条路径的长度,记为 \(res_2\) ,输出 \((n-1)\times2-res_1+1-res_2+1\)

2.Code

#define N 100010
int n,k,bg,ed,vis[N];
int res1,res2,pos,dis[N],f[N],fa[N];
struct Edge {
	int to,nxt,wei;
}e[N<<1];
int head[N],cnt=-1;
inline void ade(int u,int v,int w){
	e[++cnt].to=v,e[cnt].wei=w;
	e[cnt].nxt=head[u],head[u]=cnt;
}
//第一次求树的直径,用两次DFS 
void DFS(int now,int ff){ 
    cerr<<now<<" "<<ff<<endl;
    if(dis[now]>res1){
        res1=dis[now],pos=now;
    }
    fa[now]=ff;
    for(rg int i=head[now];~i;i=e[i].nxt){
        int v=e[i].to,w=e[i].wei;
        if(v!=ff){
            dis[v]=dis[now]+w;
            DFS(v,now);
        }
    }
}
//第二次求树的直径,用dp 
void DFS2(int now,int ff){
    for(rg int i=head[now];~i;i=e[i].nxt){
        int v=e[i].to,w=e[i].wei;
        if(v!=ff){
            DFS2(v,now);
            res2=max(res2,f[now]+f[v]+w);
            f[now]=max(f[now],f[v]+w);
        }
    }
}
//把第一条路径上的边权赋为-1 
void Modify(int now,int ff){
    for(rg int i=head[now];~i;i=e[i].nxt){
        int v=e[i].to;
        if(v!=ff){
            if(vis[v])e[i].wei=e[i^1].wei=-1;//注意把反向边也更改了 
            Modify(v,now);
        }
    }
}
int main(){
    Read(n),Read(k);
    memset(head,-1,sizeof(head));
    for(rg int i=1;i<n;i++){
        int u,v;Read(u),Read(v);
        ade(u,v,1),ade(v,u,1);
    }
    DFS(1,0),bg=pos,pos=0,res1=0;
    memset(dis,0,sizeof(dis));
    memset(fa,0,sizeof(fa));
    DFS(bg,0),ed=pos,pos=0;
    while(ed)vis[ed]=1,ed=fa[ed];//标记第一条路径 
    Modify(bg,0);
    DFS2(bg,0);
    if(k==1)cout<<(n-1)*2-res1+1<<endl;
    else cout<<(n-1)*2-res1+1-res2+1<<endl;
    return 0;
}

3.The End

posted @ 2020-12-02 20:09  ajthreac  阅读(98)  评论(1编辑  收藏  举报