zoukankan      html  css  js  c++  java
  • BZOJ1912: [Apio2010]patrol 巡逻

    又是一道看了题解的题

    题意:给出一棵边权为 1 的树,要求一次走完所有的边并回到出发点,规定你可以选择连 k 条边权为 1 的边,并且新连的边必须且只能走一次,求最短路程

    显然 k = 1 的时候答案为 (2n - 树的直径 - 1)

    现在考虑 k = 2 的时候如何连第二条边

    由于每条原来的边至少走一次,连边的策略需要尽可能保证所连接的两点及其 lca 之间的边经过次数 > 1,并选择最长的路径

    但显然不是所有情况都有 满足条件的路径 能使得答案最优的

    那接下来考虑如何在决策选点对时考虑上之前连的边的影响

    以下来自题解

    若新连出的环与之前连出的环不重叠,则答案会继续减小

    若重叠,两环重叠的部分就不会被巡逻到,又因题目要求至少经过一次,我们不得不在合适的时候经过这条边并返回

    最终的后果是两环重叠的部分由之前的“只需要经过一次”变成了“需要经过两次”

    再想一下,对于原图,每条边需要经过两次,相当于这条边变回去了

    我们这时把第一次求出的直径上的边边权取相反数

    再求一遍直径,需要注意的是这一次一定要用 dp 求树的直径,因为有负边权了, bfs 第一次找最深的节点的性质不满足,会搞炸,可以手画样例理解一下

    设两次求出的直径分别为 l1, l2

    则答案为 (2(n - 1) - (l1 - 1) - (l2 - 1)) = 2n - l1 - l2

    以下 copy 自题解

    如果 直径l2 包含 直径l1 的部分

    当减掉 (l1 - 1) 后,重叠的部分变为“只需要经过一次”,减掉 (l2 - 1) 后,重叠的部分就变回了“需要经过两次”,这正是我们想要的


    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<cstdio>
    #include<queue>
    using namespace std;
    
    const int MAXN = 100005;
    
    struct EDGE{
      int nxt, to, val;
      EDGE(int NXT = 0, int TO = 0, int VAL = 0) {nxt = NXT; to = TO; val = VAL;}
    }edge[MAXN << 1];
    int n, k, totedge = 1, mxpt, p, q, l1, l2 = 0xcfcfcfcf;
    int head[MAXN], dst[MAXN], fa[MAXN], frm[MAXN];
    bool vis[MAXN];
    
    inline void add(int x, int y, int v) {
      edge[++totedge] = EDGE(head[x], y, v);
      head[x] = totedge;
      return;
    }
    void dfs(int x) {
      vis[x] = true;
      for(int i = head[x]; i; i = edge[i].nxt) if(!vis[edge[i].to]) {
          int y = edge[i].to;
          dfs(y);
          l2 = max(l2, dst[x] + dst[y] + edge[i].val);
          dst[x] = max(dst[x], dst[y] + edge[i].val);
      }
      return;
    }
    inline void bfs(int bgn) {
      for(int i = 1; i <= n; ++i) dst[i] = 0x3f3f3f3f, fa[i] = 0, frm[i] = 0;
      queue<int> que;
      dst[bgn] = 0;
      frm[bgn] = 0;
      que.push(bgn);
      while(!que.empty()) {
        int x = que.front(); que.pop();
        if(dst[x] > dst[mxpt]) mxpt = x;
        for(int i = head[x]; i; i = edge[i].nxt) if(dst[edge[i].to] == 0x3f3f3f3f) {
          int y = edge[i].to;
          dst[y] = dst[x] + edge[i].val;
          frm[y] = i;
          fa[y] = x;
          que.push(y);
        }
      }
      return;
    }
    
    int main() {
      scanf("%d%d", &n, &k);
      register int xx, yy;
      for(int i = 1; i < n; ++i) {
        scanf("%d%d", &xx, &yy);
        add(xx, yy, 1); add(yy, xx, 1);
      }
      dst[0] = 0xcfcfcfcf;
      bfs(1); p = mxpt; mxpt = 0;
      bfs(p); q = mxpt;
      while(mxpt != p) {
        ++l1;
        edge[frm[mxpt]].val = edge[frm[mxpt] ^ 1].val = -1;
        mxpt = fa[mxpt];
      }
      if(k == 1) {
        printf("%d
    ", (n << 1) - l1 - 1);
        return 0;
      }
      if(k == 2) {
        for(int i = 1; i <= n; ++i) dst[i] = 0;
        dfs(1);
        if(l2 < 0) l2 = 0;
        printf("%d
    ", (n << 1) - l1 - l2);
      }
      return 0;
    }
    

      

    禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载 ,用户转载请注明出处:https://www.cnblogs.com/xcysblog/
  • 相关阅读:
    程序员年后离职跳槽指南
    程序员年后离职跳槽指南
    程序员为什么会有职业瓶颈?
    程序员为什么会有职业瓶颈?
    年过35岁的程序员都去哪了?一张图道尽老程序员们的花样出路
    年过35岁的程序员都去哪了?一张图道尽老程序员们的花样出路
    对于Ping的过程,你真的了解吗?
    对于Ping的过程,你真的了解吗?
    shell解析命令行的过程以及eval命令
    shell的命令替换和命令组合
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9571604.html
Copyright © 2011-2022 走看看