[树上最长链][tarjan][单调队列][环上前缀和] Jzoj P5905 黑暗之魂(darksoul)

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

 

 

题解

  • 题目大意就是在一棵有环的树上求树上最长链
  • 首先,先不考虑环,把所有在换上的子树的树上最长链求出来(PS:答案也有可能是一棵子树上的最长链+次大链)
  • 那么我们如何找到在环上的子树呢
  • 因为题目说是n条边,那么就是只有一个环,这样的话直接用tarjan就可以求出来了
  • 考虑加上环,先说明一个东西:题目中的主人公也是有脑子的,不会绕远路
  • 也就是在环上,只会走较短的劣弧不会走优弧,
  • 就可以用单调队列维护在比圆的周长的一半要小的部分中的递减序列
  • 链的长度的公式:len[x]+len[i]+pre[x]-len[i]
  • 因为还有比圆的周长的一半还要大的,所以要把数组倍长

代码

  1 #include <cstdio> 
  2 #include <iostream>
  3 #include <cstring>
  4 #include <vector>
  5 #include <algorithm>
  6 using namespace std;
  7 const int maxn=1e7+10;
  8 int n,cnt,k,bel,tot,head1,tail,boo,head[maxn],dfn[maxn],low[maxn],belong[maxn];
  9 bool vis[maxn];
 10 int root[maxn],q[maxn*2];
 11 long long ans,f[maxn*2],len[maxn*2],pre[maxn*2],dis[maxn*2],mid;
 12 struct edge{ int v,to,from; }e[maxn*2];
 13 struct node{ int x,y,v; }a[maxn];
 14 vector<int>Q;
 15 void insert(int a,int b,int c) 
 16 { 
 17     e[++cnt].to=b; e[cnt].from=head[a]; e[cnt].v=c; head[a]=cnt; 
 18     e[++cnt].to=a; e[cnt].from=head[b]; e[cnt].v=c; head[b]=cnt; 
 19 }
 20 void dfs(int x,int fa)
 21 {
 22     long long max1=0,max2=0,l=0;
 23     for (int i=head[x];i;i=e[i].from)
 24         if (belong[e[i].to]!=k&&e[i].to!=fa)
 25         {
 26             dfs(e[i].to,x);
 27             l=f[e[i].to]+e[i].v;
 28             if (l>max1) max2=max1,max1=l; else max2=max(l,max2);
 29         }
 30     ans=max(max1+max2,ans);
 31     f[x]=max1;
 32 }
 33 void tarjan(int x,int fa)
 34 {
 35     dfn[x]=low[x]=++cnt,vis[x]=1;
 36     Q.push_back(x);
 37     for (int i=head[x];i;i=e[i].from)
 38         if (e[i].to!=fa) 
 39         {
 40             if (!dfn[e[i].to])
 41             {
 42                 tarjan(e[i].to,x);
 43                 low[x]=min(low[x],low[e[i].to]);
 44             }
 45             else if (vis[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);
 46         }
 47     if (low[x]==dfn[x])
 48     {
 49         bel++;
 50         int y=0,boo=0;
 51         if (Q.back()!=x) boo=1,k=bel;
 52         while (y!=x)
 53         {
 54             y=Q.back(),Q.pop_back();
 55             belong[y]=bel,vis[y]=0;
 56             if (boo) root[++tot]=y;
 57         }
 58     }
 59 } 
 60 bool cmp(node a,node b) { return a.x==b.x?(a.y==b.y?a.v<b.v:a.y<b.y):a.x<b.x; }
 61 int main()
 62 {
 63     freopen("darksoul.in","r",stdin),freopen("darksoul.out","w",stdout);
 64     scanf("%d",&n);
 65     for (int i=1;i<=n;i++) 
 66     {
 67         scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
 68         if (a[i].x>a[i].y) swap(a[i].x,a[i].y);
 69     }
 70     sort(a+1,a+n+1,cmp);
 71     bool boo=false;
 72     for (int i=1;i<=n;i++)
 73         if (a[i].x==a[i].y||a[i].x==a[i-1].x&&a[i].y==a[i-1].y) boo=true;
 74         else insert(a[i].x,a[i].y,a[i].v);
 75     if (boo)
 76     {
 77         k=-1,dfs(1,0),printf("%lld",ans+1);
 78         return 0;
 79     }
 80     cnt=0,tarjan(1,0);
 81     for (int i=1;i<=tot;i++) dfs(root[i],0),len[i]=f[root[i]];
 82     for (int i=1;i<=tot;i++) 
 83         for (int j=head[root[i]];j;j=e[j].from) 
 84             if (e[j].to==root[i!=1?i-1:tot])
 85             {
 86                 dis[i]=(long long)e[j].v;
 87                 break;
 88             }
 89     for (int i=1;i<=tot;i++) dis[i+tot]=dis[i],len[i+tot]=len[i];
 90     for (int i=1;i<=tot*2;i++) pre[i]=pre[i-1]+dis[i];
 91     mid=pre[tot];
 92     head1=tail=q[head1]=1;
 93     for (int i=2;i<=2*tot;i++)
 94     {
 95         while (head1<=tail&&(pre[i]-pre[q[head1]]>mid/2)) head1++;
 96         if (head1<=tail) ans=max(ans,len[q[head1]]+len[i]+pre[i]-pre[q[head1]]);
 97         while (head1<=tail&&(len[i]-pre[i]>=len[q[tail]]-pre[q[tail]])) tail--;
 98         q[++tail]=i;
 99     }
100     printf("%lld",ans+1);
101 }
posted @ 2018-10-16 09:45  BEYang_Z  阅读(249)  评论(1编辑  收藏  举报