bzoj3694: 最短路(树链剖分/并查集)

  bzoj1576的帮我们跑好最短路版本23333(双倍经验!嘿嘿嘿

  这题可以用树链剖分或并查集写。树链剖分非常显然,并查集的写法比较妙,涨了个姿势,原来并查集的路径压缩还能这么用...

  首先对于不在最短路径树上的边x->y,设t为最短路径树上lca(x,y),则t到y上的路径上的点i到根的距离都可以用h[x]+dis[x][y]+h[y]-h[i](h[]为深度)来更新,因为h[i]一定,只要让h[x]+dis[x][y]+h[y]最小就行,这里用树剖直接修改整条链上的数,就可以过了。

  并查集的方法就很巧妙了...把不在最短路径树上的边找出来,按照h[x]+dis[x][y]+h[y]从小到大排序。然后按排序后的边的顺序更新答案,被更新过了的必然不会被再次更新。更新的方法就是每次两个指针从x和y一步步向t靠近并更新沿途上没更新过的点,同时用并查集记录这些更改过的点的顶部,下次更新下面跑到这里的点直接就可以跳到没修改的地方。好像感觉其实有点像树剖...

  只写了并查集,树剖的下次补(QAQ模板都不会打了已经

并查集:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<‘0‘||c>‘9‘)c==‘-‘&&(f=-1),c=getchar();
    while(c<=‘9‘&&c>=‘0‘)k=k*10+c-‘0‘,c=getchar();
    k*=f;
}
const int maxn=4010;
struct zs{int too,sum,pre;}e[500010];
struct poi{int x,y,len;}edge[500010];
int n,m,x,y,z,flag,tot,tot2;
int fq[maxn],fa[maxn],h[maxn],v[maxn],last[maxn];
void add(int x,int y,int z){e[++tot].too=y;e[tot].sum=z;e[tot].pre=last[x];last[x]=tot;}
void dfs(int x,int fa)
{
    for(int i=last[x];i;i=e[i].pre)
    if(e[i].too!=fa)h[e[i].too]=h[x]+e[i].sum,fq[e[i].too]=x,dfs(e[i].too,x);
}
bool cmp(poi a,poi b){return a.len<b.len;}
int gf(int x){return x==fa[x]?x:fa[x]=gf(fa[x]);}
int main()
{
    read(n);read(m);
    for(int i=1;i<=m;i++)
    {
        read(x);read(y);read(z);read(flag);
        if(flag)add(x,y,z),add(y,x,z);
        else edge[++tot2].x=x,edge[tot2].y=y,edge[tot2].len=z;
    }
    dfs(1,0);
    for(int i=1;i<=tot2;i++)
    edge[i].len+=h[edge[i].x]+h[edge[i].y];
    sort(edge+1,edge+1+tot2,cmp);
    for(int i=1;i<=n;i++)
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=tot2;i++)
    {
        int x=edge[i].x,y=edge[i].y,f1=gf(x),f2=gf(y),lastx=0,lasty=0;
        while(f1!=f2)
        {
            if(h[f1]<h[f2])swap(f1,f2),swap(x,y),swap(lastx,lasty);
            if(!v[x])
            {
                v[x]=i;
                if(lastx)fa[lastx]=x;
            }else if(lastx)fa[lastx]=f1;
            lastx=f1;x=fq[f1];f1=gf(x);
        }
    }
    for(int i=2;i<=n;i++)
    if(v[i])printf("%d ",edge[v[i]].len-h[i]);
    else printf("-1 ");
}

时间: 2024-10-13 16:04:30

bzoj3694: 最短路(树链剖分/并查集)的相关文章

hdu 5458 Stability(树链剖分+并查集)

Stability Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 1347    Accepted Submission(s): 319 Problem Description Given an undirected connected graph G with n nodes and m edges, with possibly r

HDU 5458 Stability (树链剖分+并查集+set)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5458 给你n个点,m条边,q个操作,操作1是删边,操作2是问u到v之间的割边有多少条. 这题要倒着做才容易,倒着加边. 因为这题最后保证所有的点一定连通,所以可以构建一棵树,树链剖分一下.要是u到v之间只有一条边,那便是一条割边.而加边的话,就是将u到v之间的边权都+1,边权等于1的话是桥,大于1的话就不是了.所以我们初始化树的时候,可以将边权初始化为1,加边操作将边权赋值为0.求u到v的割边个数的

数据结构(并查集||树链剖分):HEOI 2016 tree

[注意事项] 为了体现增强版,题目限制和数据范围有所增强: 时间限制:1.5s 内存限制:128MB 对于15% 的数据,1<=N,Q<=1000. 对于35% 的数据,1<=N,Q<=10000. 对于50% 的数据,1<=N,Q<=100000,且数据均为官方数据. 对于100% 的数据,1<=N,Q<=1000000. 请注意常数因子对于程序运行的影响. 并查集很简单,并查集就是倒序处理,表示删除一个点的标记,删除后不会再加回来,删完后,合并当前点与其

[HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]

题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个点,得到一堆联通块,我们要做的就是用原图中剩下的边把这些联通块穿起来 考虑这个点$u$在$MST$上的位置,可以知道有两种边:一种是从$u$的任意一个儿子的子树连到$u$的子树外面的,一种是在$u$的两个儿子的子树之间连接的 第一种情况: 考虑边$(u,v)$,没有进入$MST$中,那么若它是某个节

HDU 5044 (树链剖分+树状数组+点/边改查)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5044 题目大意:修改链上点,修改链上的边.查询所有点,查询所有边. 解题思路: 2014上海网赛的变态树链剖分模板题.将以往树链剖分的点&边修改和查询合在一起之后,难度上去不少. 第一个卡人点是读入优化. 第二个卡人点是树状数组.由于要查询所有点,如果使用线段树,每次都要扫到底层才能取出点值,必T无疑. 然后使用树状数组之后,树链剖分的点/边修改写法有些变动. 点查询变化不大. 边查询只要查询一下

UOJ#53. 【UR #4】追击圣诞老人 树链剖分 k短路

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ53.html 题意 给定一棵有 n 个节点的树. 每一个点有一个权值. 对于每一个 $i$ 给定三个参数 $a_i,b_i,c_i$ ,从第 $i$ 个点出发下一步能到达的点 x 需要满足以下三个要求之一: 1. x 在 $a_i$ 到 $b_i$ 的简单路径上. 2. x 在 $a_i$ 到 $c_i$ 的简单路径上. 3. x 在 $c_i$ 到 $b_i$ 的简单路径上. 问从任意一个点出发,经过

习题:树上的最短路(树链剖分优化建图)

题目 下水道的主干路由n个节点和\(n-1\)条边所组成,每条边通过它都需要一个时间\(t_i\),这种边是双向的 下水道上有一些塌陷,我们用\((l_1,r_1,l_2,r_2,c)\)来描述,表示从\(l_1\)到\(r_1\)路径上的点,到\(l_2\)和\(r_2\)路径上的任意一个点所需要的时间为\(c\),注意塌陷是单向的 求每一个点到目标节点\(k\)的最快时间 思路 到\(k\)转换\(k\)到每一个点,塌陷反过来建就好了 之后考虑塌陷 直接树链剖分暴力建图即可 代码 #incl

HDU - 6393 Traffic Network in Numazu(树链剖分+基环树)

http://acm.hdu.edu.cn/showproblem.php?pid=6393 题意 给n个点和n条边的图,有两种操作,一种修改边权,另一种查询u到v的最短路. 分析 n个点和n条边,实际上是一棵树+一个环,如果仅仅是一棵树,那么这题就是树链剖分的模板题了. 对于环来说,可以考虑先把环中一条边提取出来,然后树链剖分,修改时用线段树,单点修改和区间求和. 查询时就考虑三种情况,u走树上到v,从u经过提取出来的边再到v(两种),取最小值. 至于取出环上一条边,用并查集搞搞. #incl

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 876  Solved: 446[Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表 各种宗教,  S国的居民常常旅行.旅行时他们总