bzoj 1576 [Usaco2009 Jan]安全路经Travel

25 篇文章 0 订阅
25 篇文章 0 订阅

唔 不是很简单的题。。。

这里写图片描述

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

输出解释:

跟题中例子相同

很神奇的一道题首先。

我们可以找出来 一颗最小的树。
就拿原图举例

这里写图片描述

实线部分是 最小生成树。然后我们现在要家边使之变成 类次短的 。

 对于当前非树边(u,v),设t=lca(u,v),这条非树边可以去尝试更新t-u和t-v(但不包括t)的最短路长度

列出式子也就是
dis[u]=dis[u]+dis[v]+w[u][v]-(dis[u])

所以我们把剩下的边按照 dis[u]+dis[v]+w[u][v]排序

然后 如果 一些点被更新过了 拿第二遍 更新 定然没有第一遍 优 所以 直接跳过就好了。

想象一下 被更新过的点 一定 在一条条链上 也就是曾祖父,祖父,父亲。。神马的

所以 我们这里用并查集维护。求lca的时候 暴力求就好。

所以 要注意的是 此题 卡spfa

要用 dij+heap

第一遍写的 板。

其实挺好理解的 跟dij过程一样用优先队列维护一下

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
//by mars_ch
int n,m;
struct data{
    int f,t;
    int w,nxt;
}e[200005*2];
struct EDGE
{
    int u,v,w;
}ee[2000005];
struct Node
{
    int now,weight;
};
int read()
{
    char p=getchar();
    int s=0;
    while(p<'0' || p>'9') p=getchar();
    while(p>='0' && p<='9') s=s*10+p-'0',p=getchar();
    return s;
}
int nxt[100005];
int first[100005],tot,cnt;
int dis[100005],f[100005],bel[100005];
int fa[100005],pre[2000005],inq[1000005],intree[1000005],deep[1000005];
queue<int> q;
void add(int a,int b,int c)
{
    e[tot].f=a,e[tot].t=b;
    e[tot].w=c;
    e[tot].nxt=first[a];
    first[a]=tot++; 
}
bool operator < (Node a,Node b){return a.weight>b.weight;}
bool cmp(EDGE a,EDGE b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x] == x) return x;
    else return f[x]=find(f[x]);
}
void dijkstra(){
    memset(dis,0x3f,sizeof(dis));
    Node jy;
    jy.now=1,jy.weight=dis[1]=0;
    priority_queue<Node>pq;
    pq.push(jy);
    while(!pq.empty()){
        Node t=pq.top();pq.pop();

        for(int i=first[t.now];~i;i=e[i].nxt)
            if(dis[e[i].t]>dis[t.now]+e[i].w){
                dis[e[i].t]=dis[t.now]+e[i].w;
                jy.now=e[i].t;jy.weight=dis[e[i].t];
               pre[e[i].t]=i,fa[e[i].t]=t.now;
                deep[e[i].t]=deep[t.now]+1;
                pq.push(jy);
            }
    }
}
int main()
{
    n=read(),m=read();
    memset(first,-1,sizeof(first));
    for(int i=1;i<=m;i++){
        int a,b,c;
        a=read(),b=read(),c=read();
        add(a,b,c);
        add(b,a,c);
    }
    dijkstra();
    for(int i=1;i<=n;i++)
    {
        intree[pre[i]]=1;
    }
    for(int i=0;i<tot;i+=2)
    {
        if(!intree[i] && !intree[i+1])
        {
            ee[++cnt].u=e[i].f,ee[cnt].v=e[i].t,ee[cnt].w=dis[e[i].f]+dis[e[i].t]+e[i].w;
        }
    }
    sort(ee+1,ee+cnt+1,cmp);
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
    }
    for(int i=1;i<=cnt;i++)
    {
        int u=ee[i].u,v=ee[i].v;
        int fx=find(u),fy=find(v);
        int lastu=0,lastv=0;
        while(fx!=fy)
        {
               if(deep[fx]<deep[fy]){
                swap(fx,fy),swap(u,v),swap(lastu,lastv);
               }
               if(!bel[u]){
                    bel[u]=i;
                    if(lastu) f[lastu]=u;
               }
               else if(lastu) f[lastu]=fx;
               lastu=fx,u=fa[lastu],fx=find(u);
        }
    }
    for(int i=2;i<=n;i++)
    {
        if(bel[i])printf("%d\n",ee[bel[i]].w-dis[i]);
        else printf("-1\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值