首页 > 代码库 > bzoj3694: 最短路(树链剖分/并查集)

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 ");
}
View Code

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