【BZOJ-1576】安全路径Travel Dijkstra + 并查集

1576: [Usaco2009 Jan]安全路经Travel

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1044  Solved: 363
[Submit][Status][Discuss]

Description

Input

* 第一行: 两个空格分开的数, N和M

* 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i

Output

* 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛路的最少的时间.如果这样的路经不存在,输出-1.

Sample Input

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

输入解释:
跟题中例子相同

Sample Output

3
3
6

输出解释:

跟题中例子相同

HINT

Source

Gold

Solution

比较简单的一道题

首先我们跑一遍最短路,得到最短路树,在松弛操作的时候加一句话即可

然后我们发现对于不在最短路树上的边,如果把它加入树中,会形成一个环,它会影响这个环上除lca的其他点

样例是这样的,黑边是指最短路树上的边,加入非树红边,影响的是红点,加入非树蓝边,有影响的是蓝点

所发生的影响就是,环上点x,在加入边<u,v>会被更新成dis[u]+dis[v]+val<u,v>-dis[x]

所以要求被更新后最小,就是要求dis[u]+dis[v]+val<u,v>最小,那么我们按照这个价值对非树边排序

从权值小的开始更新。

所以这样的话,中间会更新一些重复的,我们只要保证那些重复的不被更新即可,这个显然可以利用并查集维护一下

这样时间复杂度大概是$O(nlogn+n×a(n))$

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
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;
}
#define MAXN 100010
#define MAXM 200010
int N,M;
struct EdgeNode{int next,to,t,from;}edge[MAXM<<1];
int head[MAXN],cnt=1;
void AddEdge(int u,int v,int w) {cnt++; edge[cnt].from=u; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].t=w;}
void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,w);}
#define Pa pair<int,int>
#define INF 0x7fffffff
priority_queue<Pa,vector<Pa>,greater<Pa> >q;
int dis[MAXN],fa[MAXN];
void Dijkstra(int S)
{
    for (int i=1; i<=N; i++) dis[i]=INF;
    q.push(make_pair(0,S)); dis[S]=0;
    while (!q.empty())
        {
            int now=q.top().second;
            int Dis=q.top().first;
            q.pop();
            if (Dis>dis[now]) continue;
            for (int i=head[now]; i; i=edge[i].next)
                if (Dis + edge[i].t < dis[edge[i].to])
                    dis[edge[i].to]=Dis+edge[i].t,
                    fa[edge[i].to]=now,
                    q.push(make_pair(dis[edge[i].to],edge[i].to));
        }
}
int f[MAXN];
int F(int x) {if (!f[x]) return x; else return f[x]=F(f[x]);}
struct RoadNode
{
    int u,v,t;
    bool operator < (const RoadNode & A) const
        {return t<A.t;}
}road[MAXM];
int tot,num;
int ans[MAXN];
void Add(RoadNode x)
{
    int u=x.u,v=x.v,t=x.t;
    while (F(u)!=F(v))
        {
            num++;
            if (dis[F(u)]<dis[F(v)]) swap(u,v);
            ans[F(u)]=min(ans[F(u)],t-dis[F(u)]);
            u=f[F(u)]=fa[F(u)];
        }
}
int main()
{
    N=read(),M=read();
    for (int x,y,z,i=1; i<=M; i++) x=read(),y=read(),z=read(),InsertEdge(x,y,z);
    Dijkstra(1);
    for (int i=2; i<=cnt; i+=2)
        {
            int u=edge[i].from,v=edge[i].to;
            if (fa[u]!=v && fa[v]!=u)
                road[++tot].u=u,road[tot].v=v,road[tot].t=dis[u]+dis[v]+edge[i].t;
        }
    sort(road+1,road+tot+1);
//    for (int i=1; i<=tot; i++)
//        printf("%d %d %d\n",road[i].u,road[i].v,road[i].t);
//    for (int i=1; i<=N; i++) printf("%d  ",dis[i]); puts("");
    for (int i=1; i<=N; i++) ans[i]=INF;
    for (int i=1; i<=tot && num<N-1; i++) Add(road[i]);
    for (int i=2; i<=N; i++) printf("%d\n",ans[i]==INF? -1:ans[i]);
    return 0;
}

写完就A了....

时间: 2024-10-12 04:48:40

【BZOJ-1576】安全路径Travel Dijkstra + 并查集的相关文章

【bzoj1576/Usaco2009 Jan】安全路经Travel——dijkstra+并查集

Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛路的最少的时间.如果这样的路经不存在,输出-1. Sample Input 4 5 1 2 2 1 3 2 3 4 4 3 2 1 2 4 3 输入解释: 跟题中例子相同 Sample Output 3 3 6 输出解释: 跟

bzoj 2959 长跑(LCT+BCC+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2959 [题意] n个点,提供操作:连边,修改点权,查询自定义边的方向后起点a终点b能经过的最大点权和. [思路] 对于一个边的双连通分量,显然可以将权值全部获得. 如果没有连边操作,我们只需要将一个bcc缩点后求得a->b路径上的点权和即可. 加上连边后,使用并查集代表一个bcc,如果u,v之间不连通直接连边,如果已经连通则构成一个bcc,使用并查集将LCT的所有节点合并. 注意缩点

HDU 5441——Travel——————【并查集+二分查界限】

Travel Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1313    Accepted Submission(s): 472 Problem Description Jack likes to travel around the world, but he doesn’t like to wait. Now, he is tr

HDU 5441 Travel(并查集+统计节点个数)

http://acm.hdu.edu.cn/showproblem.php?pid=5441 题意:给出一个图,每条边有一个距离,现在有多个询问,每个询问有一个距离值d,对于每一个询问,计算出有多少点对(x,y)使得在x到y的路径上没有一条边的距离大于d. 思路:只要边距离小于d,那就是可行的,直接加入并查集来维护.并查集需要维护一下树的节点个数. 将边和询问都排序离线处理. 1 #include<iostream> 2 #include<cstdio> 3 #include<

BZOJ 2333 SCOI2011 棘手的操作 并查集+可并堆

..题意概述就不写了,各位老爷如果是看着玩的可以去搜一下,如果是做题找来的也知道题干的.实际上是题干无法缩减懒得复制ORZ 首先处理一下集合的合并和单点值查询的问题.使用并查集,对于每个点记录标记d表示这个点的实际值比这个点加入并查集的时候的值w大了多少,对于每个点find的时候把这个点到代表元路径上的点的d(不包括代表元)的d加起来更新这个点的d,每一次查询某个点的当前值的时候就先find就可以直接用w+d+代表元的d(特判这个点是不是代表元)回答.特别注意为了保证正确性在merge的时候要把

HDU-5441 Travel 离线-并查集

Travel Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 4680    Accepted Submission(s): 1532 Problem Description Jack likes to travel around the world, but he doesn't like to wait. Now, he is t

BZOJ 1854 游戏(二分图匹配或并查集)

此题的二分图匹配做法很容易想,就是把属性当做s集,武器当做t集,如果该武器拥有该武器则连一条边. 那么答案就是求该二分图的最大前i个匹配.将匈牙利算法改一改,当前找不到增广路就break. 但是过这个题需要常数优化,不能每次都fillchar一遍used数组.可以用队列将使用的used点加入,然后需要初始化的时候弹出即可. # include <cstdio> # include <cstring> # include <cstdlib> # include <i

BZOJ 3362 Navigation Nightmare 带权并查集

题目大意:给定一些点之间的位置关系,求两个点之间的曼哈顿距离 此题土豪题,不过POJ也有一道同样的题,可以刷一下 别被题目坑到了,这题不强制在线,把询问离线处理即可 然后就是带权并查集的问题了...将权值设为方向向量,重载+和-,按照正常权值并查集做就行了 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 40400 using namesp

HDU 5441 Travel (并查集+数学+计数)

题意:给你一个带权的无向图,然后q(q≤5000)次询问,问有多少对城市(城市对(u,v)与(v,u)算不同的城市对,而且u≠v)之间的边的长度不超过d(如果城市u到城市v途经城市w, 那么需要城市u到城市w的长度e1≤d,同时城市w到城市v的长度e2≤d). 析:一开始的时候,题意都读错了,怎么看都不对,原来是只要最大的d小于等于x就可以,过了好几天才知道是这样..... 这个题是并查集的应用,先从d小的开始遍历,然后去判断有几个连通块,在连通块中计数,用一个数组即可,运用排列组合的知识可以知