[Usaco2009 Jan]安全路经Travel

安全路径

题意

Solution

嗯,首先既然不能经过最后一条边,那么我们考虑建出一个最短路树

然后非树边\(u,v,w\),只能影响到\(u->v\)这个路径上的点,因为只能往回跑

那么考虑怎么更新。一个显然的办法就是把边按照某种顺序排列,然后用并查集标记一下哪个点访问过了,可以做到\(O(n)\)

那么按照什么顺序排序呢?我们设一个点\(u\)到1的距离(即最短路长度)为\(dis_u\)

那么对于一条非树边\(u->v\),设路径上其中一个点为\(x\),那么这个“不经过最后一条边的次短路”的长度即为\(dis_u+dis_v+w-dis_x\)

然后我们按照\(dis_u+dis_v+w\)排序,就可以快乐维护了

code:

#include<bits/stdc++.h>
using namespace std;
struct qwq{
    int u;
    int v;
    int w;
    int nxt;
}edge[400010],e[400010];
int head[400010];
int cnt=-1;
void add(int u,int v,int w){
    edge[++cnt].nxt=head[u];
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=w;
    head[u]=cnt;
}
void add1(int u,int v,int w){
    e[++cnt].nxt=head[u];
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    head[u]=cnt;
}
struct QAQ{
    int v,w;
    bool operator <(const QAQ& tmp)const{
        return w>tmp.w;
    }
};
int dis[400010];
int vis[400010];
int n,m;
void dijkstra(int s){
    for(int i=1;i<=n;++i)dis[i]=INT_MAX;
    dis[s]=0;
    priority_queue<QAQ> q;
    q.push((QAQ){s,0});
    while(!q.empty()){
        QAQ u=q.top();
        q.pop();
        int v=u.v,w=u.w;
        if(w!=dis[v])continue;
        for(int i=head[v];~i;i=edge[i].nxt){
            int tv=edge[i].v,tw=edge[i].w;
            if(dis[tv]>dis[v]+tw){
                vis[tv]=i;
                dis[tv]=dis[v]+tw;
                q.push((QAQ){tv,dis[tv]});
            }
        }
    }
}
struct dat{
    int u,v,w;
    int du,dv;
}a[400010];
bool cmp(dat a,dat b){
    return a.w+a.du+a.dv<b.w+b.du+b.dv;
}
int tot;
int f[400010][32];
bool pd[400010];
void rebuild(){
    memset(head,-1,sizeof(head));
    int tmp=cnt;
    cnt=-1;
    for(int i=2;i<=n;++i){
        int u=edge[vis[i]].u,v=edge[vis[i]].v,w=edge[vis[i]].w;
        add1(u,v,w);
        add1(v,u,w);
        pd[vis[i]]=true;
    }
    for(int i=0;i<=tmp;++i){
        if(!pd[i]&&!pd[i^1]){
            a[++tot].u=edge[i].u;
            a[tot].v=edge[i].v;
            a[tot].w=edge[i].w;
            a[tot].du=dis[edge[i].u];
            a[tot].dv=dis[edge[i].v];
            pd[i]=true;
        }
    }
}
int dep[400010];
void dfs(int u){
    for(int i=1;i<=31;++i){
        f[u][i]=f[f[u][i-1]][i-1];
    }
    for(int i=head[u];~i;i=e[i].nxt){
        int v=e[i].v;
        if(v==f[u][0])continue;
        f[v][0]=u;
        dep[v]=dep[u]+1;
        dfs(v);
    }
}
int LCA(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    int d=dep[x]-dep[y];
    for(int i=0;i<=31;++i){
        if(d&(1<<i)){
            x=f[x][i];
        }
    }
    if(x==y){
        return x;
    }
    for(int i=31;i>=0;--i){
        if(f[x][i]==f[y][i])continue;
        x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}
int fa[400010];
int getfa(int x){
    return fa[x]==x?x:fa[x]=getfa(fa[x]);
}
int ans[400010];
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    dijkstra(1);
    rebuild();
    dfs(1);
    for(int i=1;i<=n;++i){
        fa[i]=i;
    }
    sort(a+1,a+1+tot,cmp);
    for(int i=1;i<=tot;++i){
        int u=a[i].u,v=a[i].v;
        int sum=a[i].w+a[i].du+a[i].dv;
        int lca=LCA(u,v);
        while(dep[u]>dep[lca]){
            int tmp=getfa(u);
            if(tmp==lca)break;
            if(tmp==u){
                tmp=fa[u]=f[u][0];
                ans[u]=sum;
            }
            u=tmp;
        }
        while(dep[v]>dep[lca]){
            int tmp=getfa(v);
            if(tmp==lca)break;
            if(tmp==v){
                tmp=fa[v]=f[v][0];
                ans[v]=sum;
            }
            v=fa[v];
        }
    }
    for(int i=2;i<=n;++i){
        printf("%d\n",ans[i]?ans[i]-dis[i]:-1);
    }
}

TMD考试的时候dij的堆打反了,然后T了,只有暴力分QAQ

原文地址:https://www.cnblogs.com/youddjxd/p/11768295.html

时间: 2024-12-19 15:11:21

[Usaco2009 Jan]安全路经Travel的相关文章

bzoj 1576: [Usaco2009 Jan]安全路经Travel 树链剖分

1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 665  Solved: 227[Submit][Status] Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛

【BZOJ 1576】 [Usaco2009 Jan]安全路经Travel

1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 676  Solved: 231 [Submit][Status] Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条

BZOJ1576 [Usaco2009 Jan]安全路经Travel

首先用Dijkstra做出最短路生成树,设dis[p]为1到p点的最短路长度 对于一条不在生成树上的边u -> v,不妨设fa为u.v的lca 则一fa到v的路径上的任意点x都可以由u达到,走的方式是1 -> fa -> u -> v -> x,dis'[x] = dis[u] + dis(u, v) + dis[v] - dis[x] 于是可以用dis[u] + dis(u, v) + dis[v]更新fa到v的路径上的所有点 链剖一下,线段树lazytag就好了,连pus

【BZOJ】1576 [Usaco2009 Jan]安全路经Travel

[算法]最短路树+(树链剖分+线段树)||最短路树+并查集 [题解] 两种方法的思想是一样的,首先题目限制了最短路树唯一. 那么建出最短路树后,就是询问对于每个点断掉父边后重新找路径的最小值,其它路径只能是这个点和其子树节点通过非树边到达非子树节点. 这样考虑很难统计,换个角度考虑每条非树边的影响. 一条非树边连接两个端点u,v,它们会有一个LCA,那么这条非树边就可以影响u~LCA和v~LCA两条链上的点. 这样依然不方便统计,因为两条链上每个点的影响各不相同,所以使用差分的思想. 定义一条非

[Usaco2009 Jan]安全路经Travel BZOJ1576 Dijkstra+树链剖分+线段树

分析: Dijkstra求最短路树,在最短路树上进行操作,详情可见上一篇博客:http://www.cnblogs.com/Winniechen/p/9042937.html 我觉得这个东西不压行写出了有点丑...之后写了一个压行后更丑的... 附上压行后的代码: #include <cstdio> #include <algorithm> #include <cmath> #include <cstdlib> #include <cstring>

1574: [Usaco2009 Jan]地震损坏Damage

1574: [Usaco2009 Jan]地震损坏Damage Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 425  Solved: 232[Submit][Status][Discuss] Description 农夫John的农场遭受了一场地震.有一些牛棚遭到了损坏,但幸运地,所有牛棚间的路经都还能使用. FJ的农场有P(1 <= P <= 30,000)个牛棚,编号1..P. C(1 <= C <= 100,000)条双向路经

bzoj3396[Usaco2009 Jan]Total flow 水流*

bzoj3396[Usaco2009 Jan]Total flow 水流 题意: 求无环图的最大流.边数≤700. 题解: 管它有没有环.注意本题的节点标号既有大写字母,也有小写字母. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #de

3396: [Usaco2009 Jan]Total flow 水流

3396: [Usaco2009 Jan]Total flow 水流 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 179  Solved: 73[Submit][Status] Description Input 第1行输入N,之后N行每行描述一条水管,前两个英文字母表示水管的两端(大小写字母是不一样的),后一个整数表示水管的流量,流量不会超过1000. Output 一个整数,表示总流量. Sample Input 5 A B 3 B C 3

BZOJ3394: [Usaco2009 Jan]Best Spot 最佳牧场

3394: [Usaco2009 Jan]Best Spot 最佳牧场 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 11  Solved: 9[Submit][Status] Description Input 第1行输入三个整数P,F C.之后F行每行输入一个整数表示一个贝茜喜欢的牧场.之后C行每行输入三个整数ai,bi,Ti,描述一条路. Output 一个整数,满足题目要求的最佳牧场.如果有多个答案,输出编号最小的 Sample Input