[BZOJ3242][Noi2013]快餐店 && 环套树+线段树

11 篇文章 0 订阅
7 篇文章 0 订阅

如果给的是一棵树那么答案显然是直径/2

但是现在给的是一个环套树 那你就枚举一下环上的每条边把它删掉再求直径就好辣(显然你不会走环上的某一条边 这很显然吧- - )

然后你就要求剩下来的这棵树的直径 然后n^2会爆炸 怎么办捏

我们维护以环上节点为根的树的最长链s

环上两点距离可以用前缀和处理(先假设枚举的第一条边是环上第一个点和最后一个点的边 这样就可以用前缀和表示了)

则直径为max(sum[j]-sum[i]+s[i]+s[j])

用一棵线段树维护sum[i] - s[i] 和 sum[i] + s[i]

但是i, j不能相同 所以在线段树中再维护一个次大值即可

删边的时候修改一下sum数组 修改一下线段树即可

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#define SF scanf
#define PF printf
#define ls (i<<1)
#define rs (i<<1|1)
using namespace std;
typedef long long LL;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
const int MAXN = 100000;
struct Node {
    int v, wt, next;
} Edge[MAXN*2+10];
int adj[MAXN+10], ecnt;
void addedge(int u, int v, int wt) {
    Node &e = Edge[++ecnt];
    e.v = v; e.wt = wt; e.next = adj[u]; adj[u] = ecnt;
}
bool iscir[MAXN+10];
LL sum[MAXN+10], dis[MAXN+10], d[MAXN+10], ld[MAXN+10], rd[MAXN+10], tot, ans, mx;
int cc, cir[MAXN+10], fa[MAXN+10], vis[MAXN+10], n, p;
struct Seg_Tree {
    LL mx1[MAXN*4+10], mx2[MAXN*4+10];
    int p1[MAXN*4+10], p2[MAXN*4+10];
    void up(int i) {
        if(mx1[ls] > mx1[rs]) {
            mx1[i] = mx1[ls];
            p1[i] = p1[ls];
            mx2[i] = mx1[rs];
            p2[i] = p1[rs];
        }
        else {
            mx1[i] = mx1[rs];
            p1[i] = p1[rs];
            mx2[i] = mx1[ls];
            p2[i] = p1[ls];
        }
        if(mx2[ls] > mx2[i]) {
            mx2[i] = mx2[ls];
            p2[i] = p2[ls];
        }
        if(mx2[rs] > mx2[i]) {
            mx2[i] = mx2[rs];
            p2[i] = p2[rs];
        }
    }
    void build(int i, int l, int r) {
        if(l == r) {
            mx1[i] = sum[l] + dis[l]; p1[i] = l;
            mx2[i] = p2[i] = 0;
            return ;
        }
        int mid = l+r >> 1;
        build(ls, l, mid);
        build(rs, mid+1, r);
        up(i);
    }
    void ins(int i, int x, LL v, int L = 1, int R = cc) {
        if(L == R) {
            mx1[i] = v;
            return ;
        }
        int mid = L+R >> 1;
        if(x <= mid) ins(ls, x, v, L, mid);
        else ins(rs, x, v, mid+1, R);
        up(i);
    }
} T;
struct Seg_Tree2 {
    LL mx1[MAXN*4+10], mx2[MAXN*4+10];
    int p1[MAXN*4+10], p2[MAXN*4+10];
    void up(int i) {
        if(mx1[ls] > mx1[rs]) {
            mx1[i] = mx1[ls];
            p1[i] = p1[ls];
            mx2[i] = mx1[rs];
            p2[i] = p1[rs];
        }
        else {
            mx1[i] = mx1[rs];
            p1[i] = p1[rs];
            mx2[i] = mx1[ls];
            p2[i] = p1[ls];
        }
        if(mx2[ls] > mx2[i]) {
            mx2[i] = mx2[ls];
            p2[i] = p2[ls];
        }
        if(mx2[rs] > mx2[i]) {
            mx2[i] = mx2[rs];
            p2[i] = p2[rs];
        }
    }
    void build(int i, int l, int r) {
        mx1[i] = mx2[i] = -10000000000000000LL;
        if(l == r) {
            mx1[i] = dis[l] - sum[l]; p1[i] = l;
            return ;
        }
        int mid = l+r >> 1;
        build(ls, l, mid);
        build(rs, mid+1, r);
        up(i);
    }
    void ins(int i, int x, LL v, int L = 1, int R = cc) {
        if(L == R) {
            mx1[i] = v;
            return ;
        }
        int mid = L+R >> 1;
        if(x <= mid) ins(ls, x, v, L, mid);
        else ins(rs, x, v, mid+1, R);
        up(i);
    }
} Q;
bool dfs(int u, int pre) {
    fa[u] = pre;
    vis[u] = 1;
    for(int i = adj[u]; i; i = Edge[i].next) {
        int v = Edge[i].v;
        if(v == pre) continue;
        if(vis[v]) {
            cir[1] = v;
            fa[v] = u;
            return true;
        }
        if(dfs(v, u)) return true;
    }
    return false;
}
void Find_circle() {
    dfs(1, 0);
    int u = cir[1];
    while(fa[u] != cir[1]) {
        cir[++cc] = fa[u];
        u = fa[u];
    }
    for(int i = 1; i <= cc; i++) {
        iscir[cir[i]] = true;
        for(int j = adj[cir[i]]; j; j = Edge[j].next) {
            int v = Edge[j].v, wt = Edge[j].wt;
            if(v == cir[i%cc+1]) {
                ld[i] = rd[i%cc+1] = wt;
                tot += wt;
                break;
            }
        }
    }
    for(int i = 2; i <= cc; i++) sum[i] = sum[i-1] + rd[i];
}
void DFS(int u, int pre) {
    for(int i = adj[u]; i; i = Edge[i].next) {
        int v = Edge[i].v, wt = Edge[i].wt;
        if(v == pre || iscir[v]) continue;
        d[v] = d[u] + wt;
        if(d[v] > d[p]) p = v;
        DFS(v, u);
    }
}
void Find_dis(int x) {
    int tmp;
    d[cir[x]] = p = 0;
    DFS(cir[x], 0);
    tmp = p;
    dis[x] = d[p];
    iscir[cir[x]] = false;
    d[tmp] = p = 0;
    DFS(tmp, 0);
    ans = max(ans, d[p]);
    iscir[cir[x]] = true;
}
int main() {
    n = read();
    for(int i = 1; i <= n; i++) {
        int u, v, wt;
        u = read(); v = read(); wt = read();
        addedge(u, v, wt); addedge(v, u, wt);
    }
    Find_circle();
    for(int i = 1; i <= cc; i++) 
        Find_dis(i);
    T.build(1, 1, cc);
    Q.build(1, 1, cc);
    LL ANS = 10000000000000000LL;
    for(int i = 1; i <= cc; i++) {
        if(T.p1[1] != Q.p1[1]) ANS = min(ANS, T.mx1[1] + Q.mx1[1]);
        else ANS = min(ANS, max(T.mx1[1]+Q.mx2[1], T.mx2[1]+Q.mx1[1]));
        sum[i] = rd[i] + sum[(i>1) ? (i-1) : cc];
        T.ins(1, i, dis[i]+sum[i], 1, cc);
        Q.ins(1, i, dis[i]-sum[i], 1, cc);
    }
    PF("%.1lf\n", (double)max(ans, ANS)/ (double)2.0);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值