zoukankan      html  css  js  c++  java
  • [洛谷P3629][题解][APIO2010]巡逻

    0.Description

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

    1.Solution

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

    加一条边

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

    加两条边

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

    总结

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

  • 相关阅读:
    冒泡排序
    对于Random.Next()生成随机数重复的问题
    二分法查找
    工厂方法模式
    简单的工厂模式
    unity AssetBundle打包及加载
    关于谷歌不然安装除商店之外的拓展应用解决
    【Unity与23种设计模式】抽象工厂模式(Abstract Factory)
    【Unity与23种设计模式】解释器模式(Interpreter)
    【Unity与23种设计模式】原型模式(Prototype)
  • 原文地址:https://www.cnblogs.com/juruoajh/p/14075987.html
Copyright © 2011-2022 走看看