• 「BZOJ 3242」「NOI 2013」快餐店「基环树」


    题意

    基环树上找到一个点(可以在边上)使得它到树上最远点的距离最小,输出最小距离

    题解

    如果是一棵树,答案就是树的直径(/2)

    如果是基环树,那么很好证明删去环上的某一条边是不影响答案的。于是断环为链,单调队列维护(dep+sum,dep-sum)的最大值和次大值,然后算直径,如果两个最大值是同个结点就取一个次大,否则都取最大。

    #include <algorithm>
    #include <cstdio>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 1e5 + 10;
    
    struct Edge {
        int v, w, nxt;
    } e[N << 1];
    int hd[N], p;
    void link(int u, int v, int w) {
        e[p] = (Edge) {v, w, hd[u]};
        hd[u] = p ++;
    }
    
    int n, dfn[N], idx;
    int fa[N], fw[N], c[N], d[N], cnt;
    bool cir[N];
    
    void dfs(int u, int cur = -1) {
        dfn[u] = ++ idx;
        for(int i = hd[u]; ~ i; i = e[i].nxt) if(i != cur) {
            int v = e[i].v, w = e[i].w;
            if(!dfn[v]) {
                fa[v] = u; fw[v] = w; dfs(v, i ^ 1);
            } else if(dfn[v] < dfn[u]) {
                cnt ++; cir[c[cnt] = u] = 1; d[cnt] = w;
                for(int j = u; j != v; j = fa[j]) {
                    cnt ++; cir[c[cnt] = fa[j]] = 1; d[cnt] = fw[j];
                }
            }
        }
    }
    
    ll mdep[N][2], tree_d;
    
    void dfs2(int u, int f = -1) {
        for(int i = hd[u]; ~ i; i = e[i].nxt) {
            int v = e[i].v;
            if(v == f || cir[v]) continue ;
            dfs2(v, u);
            ll dis = e[i].w + mdep[v][0];
            if(dis > mdep[u][0]) {
                mdep[u][1] = mdep[u][0];
                mdep[u][0] = dis;
            } else if(dis > mdep[u][1]) {
                mdep[u][1] = dis;
            }
        }
        tree_d = max(tree_d, mdep[u][0] + mdep[u][1]);
    }
    
    int main() {
        scanf("%d", &n);
        fill(hd + 1, hd + n + 1, -1);
        for(int u, v, w, i = 1; i <= n; i ++) {
            scanf("%d%d%d", &u, &v, &w);
            link(u, v, w); link(v, u, w);
        }
        dfs(1);
        static ll dep[N << 1], sum[N << 1], ans = 1ll << 62;
        for(int i = 1; i <= cnt; i ++) {
            dfs2(c[i]);
            dep[i] = dep[i + cnt] = mdep[c[i]][0];
        }
        for(int i = 1; i <= cnt << 1; i ++)
            sum[i] = sum[i - 1] + d[i > cnt ? i - cnt : i];
    
        static int q1[N << 1], l1, r1;
        static int q2[N << 1], l2, r2;
        #define val1(u) dep[u] - sum[u]
        #define val2(u) dep[u] + sum[u]
        for(int i = 1; i <= cnt << 1; i ++) {
            for(; l1 < r1 && q1[l1] + cnt - 1 < i; l1 ++) ;
            for(; r1 - l1 > 1 && q1[l1 + 1] + cnt - 1 < i; l1 ++) q1[l1 + 1] = q1[l1];
            for(; l2 < r2 && q2[l2] + cnt - 1 < i; l2 ++) ;
            for(; r2 - l2 > 1 && q2[l2 + 1] + cnt - 1 < i; l2 ++) q2[l2 + 1] = q2[l2];
    
            for(; r1 - l1 > 2 && val1(q1[r1 - 1]) <= val1(i); r1 --) ;
            q1[r1 ++] = i;
            if(r1 - l1 <= 3) {
                for(int j = r1 - 1; j > l1; j --)
                    if(val1(q1[j]) > val1(q1[j - 1])) swap(q1[j], q1[j - 1]);
            }
            for(; r2 - l2 > 2 && val2(q2[r2 - 1]) <= val2(i); r2 --) ;
            q2[r2 ++] = i;
            if(r2 - l2 <= 3) {
                for(int j = r2 - 1; j > l2; j --)
                    if(val2(q2[j]) > val2(q2[j - 1])) swap(q2[j], q2[j - 1]);
            }
            if(i >= cnt && r1 - l1 > 1) {
                int a1 = q1[l1], a2 = q1[l1 + 1];
                int b1 = q2[l2], b2 = q2[l2 + 1];
                ll cir_d = 0;
                if(a1 == b1) cir_d = max(val1(a1) + val2(b2), val1(a2) + val2(b1));
                else cir_d = val1(a1) + val2(b1);
                ans = min(ans, max(tree_d, cir_d));
            }
        }
        printf("%lld.%c
    ", ans >> 1, ans & 1 ? '5' : '0');
        return 0;
    }
    
    

    ( ext{Codeforces 835F})是一样的题,数据范围乘(2),改一下输出就行.

    什么,( ext{Wrong Answer})

    我觉得这是一个错误的解法,回头有时间更新一种用前后缀的做法。

    如果我几个月都没更 可以在评论里捶我

  • 相关阅读:
    使用Razor视图引擎来生成邮件内容
    Asp .Net Core 2.0 登录授权以及多用户登录
    简单几步,提升.Net Core的开发效率
    百万数据测试 Entity Framework 到底有多慢
    纸壳CMS(ZKEACMS)体验升级,快速创建页面,直接在页面中修改内容
    ZKEACMS 配置使用 HTTPS
    使用 jQuery.Pin 垂直滚动时固定导航
    底层的增删查改
    关于hangfire的使用
    巧用 CSS 实现酷炫的充电动画
  • 原文地址:https://www.cnblogs.com/hongzy/p/10363119.html
Copyright © 2020-2023  润新知