zoukankan      html  css  js  c++  java
  • [APIO2010]巡逻

    https://zybuluo.com/ysner/note/1164854

    题面

    给定一颗树,现可加入(k)(k=1,2))条边,问在加边后,从(1)号节点出发走遍所有点再回到(1)号点最小距离是多少?

    解析

    (大前提:加入虚边,统计实边)
    (k)的范围耐人寻味,可以尝试用分类讨论完成此题。

    (k=1)

    没加边时,最小距离是边数×2
    要减距离就从这些 本来就存在的边上 来。
    加了一条虚边(不是自环)后,整个图转变为一个环套树。
    环越大,距离越小。
    如何让环的大小最大?
    把树的直径两端连起来即可。
    这时最小距离改变为环的大小+环外边数×2
    减少的距离为环的大小-2(因在加边之前,环其它边对距离的贡献为(环的大小-1)×2,加完后变为环的大小)。
    同时等价于树的直径-1

    (k=2)

    这时我们要在环套树上加一条虚边。
    再加一条边,会引起什么改变呢?

    (step 1) 虚边两端点都在环外

    这肯定是更优的,减少的距离还是树的直径-1。((1)代表新加入的边)

    (step 2) 虚边一端点在环内

    这样会形成两个有公共边的环。
    要符合要求地走完它,就要额外把公共边多走一遍。(画图理解)
    则距离减少量为-公共边数量+新纳入环的实边数-1((1)代表新加入的边)

    (step 3) 虚边两端点都在环内

    这种情况貌似是上一种的特殊情况。
    因为新纳入环的实边数=0
    所以这种做法是不存在的。

    实现

    发现(step 1)(step 2)式子可以合并,(so happy...)
    肯定先找一遍树的直径。
    然后把在环中的边权标为(-1),再找一遍。
    注意到这次就不能用bfs找直径了,只能用DP,反正不用找出树的直径上的边
    找出的直径是不是正数,说明加边肯定亏,那就加自环吧。

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=1e5+100;
    int n,k,h[N],cnt=1,mx,pos,dis[N],ans;
    bool vis[N];
    struct Edge{int to,next,w;}e[N<<1];
    il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u],1};h[u]=cnt;}
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void dpdp(re int u)
    {
      vis[u]=1;
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(vis[v]) continue;
          dpdp(v);
          mx=max(mx,dis[u]+dis[v]+e[i].w);
          dis[u]=max(dis[u],dis[v]+e[i].w);
        }
    }     
    il void dfs(re int u,re int len)
    {
      vis[u]=1;if(len>mx) mx=len,pos=u;dis[u]=len;
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(vis[v]) continue;
          dfs(v,len+e[i].w);
        }
      vis[u]=0;
    }
    il int dfsb(re int u)
    {
      if(!dis[u]) return 1;
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(dis[v]==dis[u]-1)
        if(dfsb(v)) {e[i].w=e[i^1].w=-1;return 1;}
        }
      return 0;
    }
    int main()
    {
      memset(h,-1,sizeof(h));
      n=gi();k=gi();ans=(n-1)*2;
      fp(i,1,n-1)
        {
          re int u=gi(),v=gi();
          add(u,v);add(v,u);
        }
      dfs(pos=1,0);
      mx=-1e9;memset(dis,0,sizeof(dis));
      dfs(pos,0);//printf("%d
    ",pos);
      ans-=mx-1;
      if(k==1) {printf("%d
    ",ans);return 0;}
      dfsb(pos);mx=0;memset(vis,0,sizeof(vis));memset(dis,0,sizeof(dis));
      dpdp(1);
        ans-=mx-1;
      printf("%d
    ",ans);
      return 0;
    }
    
  • 相关阅读:
    软件工程第一次作业
    Python正则表达式学习摘要
    个人作业——软件工程实践总结作业
    个人作业——软件评测
    软件工程实践2019第五次作业--结对编程的实现
    软件工程实践2019第四次作业
    软件工程实践2019第三次作业
    软件工程实践2019第二次作业
    软件工程实践2019第一次作业
    个人作业——软件工程实践总结&个人技术博客
  • 原文地址:https://www.cnblogs.com/yanshannan/p/9108326.html
Copyright © 2011-2022 走看看