[tarjan][树上最长链]JZOJ 5905 黑暗之魂

Description

oi_juruo热爱一款名叫黑暗之魂的游戏。在这个游戏中玩家要操纵一名有 点生命值的无火的余灰在一张地图中探险。地图中有n个篝火(也就是存档点)。在篝火处休息可以将生命值恢复满。每个篝火都会向其他篝火的其中之一连有一条通道(显然,通道是双向的),这些篝火之间都相互可达。也就是说,这是一张n个点,n条边的无向连通图。每条通道里都有一些怪物,经过oi_juruo的分析,他得到了每条边的怪物会对他造成的伤害值 .为了向oier们表演他高超的游戏技巧,他要从任意一个篝火跑到任意另一个篝火而不在之间的篝火休息,在此期间,他会和他经过的通道中的怪物战斗并损失 的生命值。现在oi_juruo想知道,他的生命值 至少为多少,才能完成任意一条旅途。oi_juruo并不傻,他会走最安全的路。本题时限为3000ms
 

Input

第一行一个整数n。之后n行,每行三个整数ui,vi,ai ,表示有一条从ui 连向vi ,怪物伤害值为ai 的通道。

Output

一行一个数hp,表示无火的余灰的最小生命值。
 

Sample Input

5
1 2 2
2 3 2
3 4 2
1 4 1
4 5 4
 

Sample Output

8

样例说明
从2到5的路最危险,2 1 4 5受到了7点伤害,所以需要有8点生命值。
 

Data Constraint

分析

因为只有n条边且图连通,所以这是一个环套树(重边和自环特判)

容易联想到这是一个由环贯穿的森林

那么答案可能在森林里的树上最长链里,也有可能要走一段环的路

树上最长链好说,但是走环的路怎么搞呢

我们设f[i]为以i为根的子树内,从i出发的一条最长链的长度

那么再把环上的距离算出来(前缀和,数组为s吧)

则ans=max{f[i]+f[j]+s[i]-s[j] (i>j)

然后我们可以枚举i,用单调队列来维护-s[j]+f[j]即可

 

#include <iostream>
#include <cstdio>
#include <deque>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e6+10;
struct Edge {
    int u,v,nx;
    ll w;
}g[N*2];
int cnt,list[N];
struct Aedge {
    int u,v;
    ll w;
    bool operator < (const Aedge a) const {
        return u<a.u||u==a.u&&v<a.v||u==a.u&&v==a.v&&w<a.w;
    }
}a[N];
ll f[N],ans,l[N*2],s[N*2];
int low[N],dfn[N],tme;
int lcnt,rcnt,bel[N],root[N];
int cirnum;
bool instk[N];
int n;
stack<int> stk;

void Add(int u,int v,ll w) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].w=w;g[cnt].nx=list[u];list[u]=cnt;
    g[++cnt].u=v;g[cnt].v=u;g[cnt].w=w;g[cnt].nx=list[v];list[v]=cnt;
}

void Dfs(int u,int fa) {
    ll max1=0,max2=0;
    for (int i=list[u];i;i=g[i].nx)
        if (g[i].v!=fa&&bel[g[i].v]!=cirnum) {
            Dfs(g[i].v,u);
            ll len=f[g[i].v]+g[i].w;
            if (len>max1) max2=max1,max1=len;
            else max2=max(max2,len);
        }
    ans=max(ans,max1+max2);
    f[u]=max1;
}

void Tarjan(int u,int fa) {
    low[u]=dfn[u]=++tme;stk.push(u);instk[u]=1;
    for (int i=list[u];i;i=g[i].nx)
        if (g[i].v!=fa) {
            if (!dfn[g[i].v]) {
                Tarjan(g[i].v,u);
                low[u]=min(low[u],low[g[i].v]);
            }
            else if (instk[g[i].v]) low[u]=min(low[u],dfn[g[i].v]);
        }
    if (low[u]==dfn[u]) {
        int o;
        bool iscir=stk.top()!=u?1:0;
        lcnt++;
        do {
            o=stk.top();stk.pop();
            bel[o]=lcnt;instk[o]=0;
            if (iscir) root[++rcnt]=o,cirnum=lcnt;
        }
        while (!stk.empty()&&o!=u);
    }
}

int main() {
    freopen("darksoul.in","r",stdin);
    freopen("darksoul.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) {
        scanf("%d%d%lld",&a[i].u,&a[i].v,&a[i].w);
        if (a[i].u>a[i].v) swap(a[i].u,a[i].v);
    }
    sort(a+1,a+n+1);
    bool isatree=0;
    for (int i=1;i<=n;i++)
        if (a[i].u==a[i].v||a[i].u==a[i-1].u&&a[i].v==a[i-1].v) isatree=1;
        else Add(a[i].u,a[i].v,a[i].w);
    if (isatree) {
        cirnum=-1;Dfs(1,0);printf("%lld",ans+1);
        return 0;
    }
    Tarjan(1,0);
    for (int i=1;i<=rcnt;i++) Dfs(root[i],0),l[i]=f[root[i]];
    for (int i=1;i<=rcnt;i++)
        for (int j=list[root[i]];j;j=g[j].nx)
            if (g[j].v==root[i==1?rcnt:i-1]) {
                s[i]=g[j].w;
                break;
            }
    for (int i=1;i<=rcnt;i++) s[i+rcnt]=s[i],l[i+rcnt]=l[i];
    for (int i=1;i<=rcnt*2;i++) s[i]+=s[i-1];
    deque<int> q;
    while (!q.empty()) q.pop_back();
    q.push_back(1);
    for (int i=2;i<=rcnt*2;i++) {
        while (!q.empty()&&s[i]-s[q.front()]>s[rcnt]/2) q.pop_front();
        if (!q.empty()) ans=max(ans,l[i]+l[q.front()]+s[i]-s[q.front()]);
        while (!q.empty()&&-s[i]+l[i]>=-s[q.back()]+l[q.back()]) q.pop_back();
        q.push_back(i);
    }
    printf("%lld",ans+1);
}
View Code

 

posted @ 2018-10-16 21:42  Vagari  阅读(229)  评论(0编辑  收藏  举报